Browse Source

【第一版开发】

1.修改配置
2.页面开发
ChenYL 9 months ago
parent
commit
e173253573

+ 9 - 5
.vscode/launch.json

@@ -1,14 +1,18 @@
 {
-    // 使用 IntelliSense 了解相关属性。 
-    // 悬停以查看现有属性的描述。
-    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
     "version": "0.2.0",
     "configurations": [
         {
             "type": "node-terminal",
-            "name": "运行脚本: dev:mp-weixin",
+            "name": "开发环境",
             "request": "launch",
-            "command": "npm run dev:mp-weixin",
+            "command": "npm run dev:custom wp-dev",
+            "cwd": "${workspaceFolder}"
+        },
+        {
+            "type": "node-terminal",
+            "name": "SIT测试环境",
+            "request": "launch",
+            "command": "npm run dev:custom wp-sit",
             "cwd": "${workspaceFolder}"
         }
     ]

+ 16 - 0
.vscode/tasks.json

@@ -0,0 +1,16 @@
+{
+	"version": "2.0.0",
+	"tasks": [
+		{
+			"type": "npm",
+			"script": "build:custom wp-prod",
+			"group": {
+				"kind": "build",
+				"isDefault": true
+			},
+			"problemMatcher": [],
+			"label": "生产环境打包",
+			"detail": "npm run build:custom wp-prod"
+		}
+	]
+}

+ 37 - 28
package.json

@@ -3,35 +3,44 @@
   "version": "0.0.0",
   "scripts": {
     "dev:custom": "uni -p",
-    "dev:h5": "uni",
-    "dev:h5:ssr": "uni --ssr",
-    "dev:mp-alipay": "uni -p mp-alipay",
-    "dev:mp-baidu": "uni -p mp-baidu",
-    "dev:mp-jd": "uni -p mp-jd",
-    "dev:mp-kuaishou": "uni -p mp-kuaishou",
-    "dev:mp-lark": "uni -p mp-lark",
-    "dev:mp-qq": "uni -p mp-qq",
-    "dev:mp-toutiao": "uni -p mp-toutiao",
-    "dev:mp-weixin": "uni -p mp-weixin",
-    "dev:mp-xhs": "uni -p mp-xhs",
-    "dev:quickapp-webview": "uni -p quickapp-webview",
-    "dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
-    "dev:quickapp-webview-union": "uni -p quickapp-webview-union",
+    "dev:custom wp-dev": "npm run dev:custom wp-dev",
+    "dev:custom wp-sit": "npm run dev:custom wp-sit",
+    "dev:custom wp-uat": "npm run dev:custom wp-uat",
     "build:custom": "uni build -p",
-    "build:h5": "uni build",
-    "build:h5:ssr": "uni build --ssr",
-    "build:mp-alipay": "uni build -p mp-alipay",
-    "build:mp-baidu": "uni build -p mp-baidu",
-    "build:mp-jd": "uni build -p mp-jd",
-    "build:mp-kuaishou": "uni build -p mp-kuaishou",
-    "build:mp-lark": "uni build -p mp-lark",
-    "build:mp-qq": "uni build -p mp-qq",
-    "build:mp-toutiao": "uni build -p mp-toutiao",
-    "build:mp-weixin": "uni build -p mp-weixin",
-    "build:mp-xhs": "uni build -p mp-xhs",
-    "build:quickapp-webview": "uni build -p quickapp-webview",
-    "build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
-    "build:quickapp-webview-union": "uni build -p quickapp-webview-union"
+    "build:custom wp-uat": "npm run build:custom wp-uat",
+    "build:custom wp-prod": "npm run build:custom wp-prod"
+  },
+  "uni-app": {
+    "scripts": {
+      "wp-dev": {
+        "title": "微信小程序 开发环境",
+        "env": {
+          "UNI_PLATFORM": "mp-weixin",
+          "BASE_API_URL": "http://localhost:8080"
+        }
+      },
+      "wp-sit": {
+        "title": "微信小程序 sit-测试环境",
+        "env": {
+          "UNI_PLATFORM": "mp-weixin",
+          "BASE_API_URL": "http://192.168.123.242:8080"
+        }
+      },
+      "wp-uat": {
+        "title": "微信小程序 uat-测试环境",
+        "env": {
+          "UNI_PLATFORM": "mp-weixin",
+          "BASE_API_URL": "https://api-dataeasy.20261001.xyz"
+        }
+      },
+      "wp-prod": {
+        "title": "微信小程序 prod-生产环境",
+        "env": {
+          "UNI_PLATFORM": "mp-weixin",
+          "BASE_API_URL": "https://api-dataeasy.zhixinghe1.top"
+        }
+      }
+    }
   },
   "dependencies": {
     "@dcloudio/uni-app": "3.0.0-4040520250104002",

+ 9 - 0
src/apis/apis.js

@@ -0,0 +1,9 @@
+import * as userApi from './userApi.js';
+import * as subscriptionApi from './subscriptionApi.js'
+import * as orderApi from './orderApi.js'
+
+export {
+    userApi,
+    subscriptionApi,
+    orderApi
+}

+ 14 - 0
src/apis/orderApi.js

@@ -0,0 +1,14 @@
+import request from "@/utils/request";
+
+/**
+ * 创建订单
+ * @param {Object} data
+ */
+export function createOrder(data) {
+    return request({
+        url: "/order/createOrder",
+        method: "post",
+        data,
+        loading: true
+    });
+}

+ 52 - 0
src/apis/subscriptionApi.js

@@ -0,0 +1,52 @@
+import request from "@/utils/request";
+
+/**
+ * 查询订阅源
+ * @param {Object} data
+ */
+export function querySubscriptionSource(data) {
+    return request({
+        url: "/subscription/querySubscriptionSource",
+        method: "get",
+        loading: true
+    });
+}
+
+/**
+ * 查询指定订阅源详情
+ * @param {Object} data
+ */
+export function querySubscriptionSourceDetail(data) {
+    return request({
+        url: "/subscription/querySubscriptionSourceDetail",
+        method: "get",
+        data,
+        loading: true
+    });
+}
+
+/**
+ * 查询用户已订阅的订阅源
+ * @param {Object} data
+ */
+export function querySubscriptionUserConfig(data) {
+    return request({
+        url: "/subscription/querySubscriptionUserConfig",
+        method: "get",
+        data,
+        loading: true
+    });
+}
+
+/**
+ * 修改消息推送选项
+ * @param {Object} data
+ */
+export function modifyPushOption(data) {
+    return request({
+        url: "/subscription/modifyPushOption",
+        method: "post",
+        data,
+        loading: true
+    });
+}

+ 38 - 0
src/apis/userApi.js

@@ -0,0 +1,38 @@
+import request from "@/utils/request";
+
+/**
+ * 登录
+ * @param {Object} data
+ */
+export function login(data) {
+    return request({
+        url: "/user/login",
+        method: "post",
+        data,
+        loading: true,
+        loadingText: "登录中..."
+    });
+}
+
+/**
+ * 查询用户信息
+ */
+export function queryUserInfo() {
+    return request({
+        url: "/user/queryUserInfo",
+        method: "get"
+    });
+}
+
+/**
+ * 修改昵称
+ */
+export function modifyNickname(data) {
+    return request({
+        url: "/user/modifyNickname",
+        method: "post",
+        data,
+        loading: true,
+        loadingText: '正在修改...'
+    });
+}

+ 12 - 0
src/common/cache.js

@@ -0,0 +1,12 @@
+/**
+ * 缓存
+ */
+const cacheKey = {
+
+    /**
+     * 登录凭据
+     */
+    TOKEN: "token"
+}
+
+export default cacheKey;

+ 32 - 0
src/common/router.js

@@ -0,0 +1,32 @@
+/**
+ * 页面路由
+ */
+const router = {
+
+    /**
+     * 登录页
+     */
+    LOGIN_PAGE: '/pages/login',
+
+    /**
+     * 订阅源
+     */
+    SUBSCRIPTION_SOURCE_MARKET_PAGE: '/pages/subscriptionSourceMarket',
+
+    /**
+     * 订阅源详情
+     */
+    SUBSCRIPTION_SOURCE_DETAIL_PAGE: '/pages/subscriptionSourceDetail',
+
+    /**
+     * 用户的订阅源配置详情
+     */
+    SUBSCRIPTION_USER_CONFIG_PAGE: '/pages/subscriptionUserConfig',
+
+    /**
+     * 用户中心
+     */
+    USER_CENTER_PAGE: 'pages/userCenter'
+}
+
+export default router;

+ 1 - 1
src/manifest.json

@@ -50,7 +50,7 @@
     "quickapp" : {},
     /* 小程序特有相关 */
     "mp-weixin" : {
-        "appid" : "",
+        "appid" : "wxb047fb93d52125af",
         "setting" : {
             "urlCheck" : false
         },

+ 16 - 13
src/pages.json

@@ -7,27 +7,30 @@
 	},
 	"pages": [
 		{
-			"path": "pages/market",
+			"path": "pages/subscriptionSourceMarket",
 			"style": {
-				"navigationBarTitleText": "订阅市场"
+				"navigationBarTitleText": "订阅源市场",
+				"enablePullDownRefresh": true
 			}
 		},
 		{
-			"path": "pages/subscribeList",
+			"path": "pages/subscriptionSourceDetail",
 			"style": {
-				"navigationBarTitleText": "已订阅"
+				"navigationBarTitleText": "订阅源详情",
+				"enablePullDownRefresh": true
 			}
 		},
 		{
-			"path": "pages/userInfo",
+			"path": "pages/subscriptionUserConfig",
 			"style": {
-				"navigationBarTitleText": "我的"
+				"navigationBarTitleText": "已订阅源",
+				"enablePullDownRefresh": true
 			}
 		},
 		{
-			"path": "pages/detail/productHuntDetail",
+			"path": "pages/userCenter",
 			"style": {
-				"navigationBarTitleText": "ProductHunt每日热棒"
+				"navigationBarTitleText": "我的"
 			}
 		},
 		{
@@ -44,19 +47,19 @@
 		"backgroundColor": "#ffffff",
 		"list": [
 			{
-				"pagePath": "pages/market",
+				"pagePath": "pages/subscriptionSourceMarket",
 				"iconPath": "static/market.png",
 				"selectedIconPath": "static/market_active.png",
-				"text": "市场"
+				"text": "订阅源市场"
 			},
 			{
-				"pagePath": "pages/subscribeList",
+				"pagePath": "pages/subscriptionUserConfig",
 				"iconPath": "static/subscribe.png",
 				"selectedIconPath": "static/subscribe_active.png",
-				"text": "已订阅"
+				"text": "已订阅"
 			},
 			{
-				"pagePath": "pages/userInfo",
+				"pagePath": "pages/userCenter",
 				"iconPath": "static/user.png",
 				"selectedIconPath": "static/user_active.png",
 				"text": "我的"

+ 0 - 185
src/pages/detail/productHuntDetail.vue

@@ -1,185 +0,0 @@
-<template>
-  <view class="bg-box">
-
-    <view class="placehold"></view>
-
-    <!-- 标题部分 -->
-    <view class="bg-item">
-      <view class="title">Product Hunt每日榜单</view>
-      <view class="description">通过微信渠道快速推送 Product Hunt 前一天的 Top30 产品的中文信息摘要</view>
-    </view>
-
-    <!-- 推送部分 -->
-    <view class="mt-24 bg-item">
-      <view class="title">推送</view>
-      <view class="description">推送渠道:XXX服务号</view>
-      <view class="description">推送频率:北京时间每天下午 4 点</view>
-    </view>
-
-    <!-- 订阅时间部分 -->
-    <view class="mt-24 bg-item">
-      <view class="title">订阅</view>
-      <view class="price-table">
-        <view class="price-item price-border">
-          <view class="item">订阅时长</view>
-          <view class="item">价格</view>
-          <view class="item"></view>
-        </view>
-        <view class="price-item price-border">
-          <view class="item">7天</view>
-          <view class="item price">1元</view>
-          <view class="item">
-            <view class="buy-btn">购买</view>
-          </view>
-        </view>
-        <view class="price-item price-border">
-          <view class="item">7天</view>
-          <view class="item price">1元</view>
-          <view class="item">
-            <view class="buy-btn">购买</view>
-          </view>
-        </view>
-        <view class="price-item price-border">
-          <view class="item">7天</view>
-          <view class="item price">1元</view>
-          <view class="item">
-            <view class="buy-btn">购买</view>
-          </view>
-        </view>
-      </view>
-    </view>
-
-    <!-- 详情部分 -->
-    <view class="mt-24 bg-item">
-      <view class="title">详情</view>
-      <view class="description">Product Hunt 是全球最大的软件产品打榜平台,每天都有开发者发布新品并为 loved 产品投票。作为聚焦创新产品的热门网站,Product Hunt
-        吸引了大量开发者和产品爱好者查看全球最新创意。
-      </view>
-      <view class="description">然而,由于其为英文网站且访问需借助网络工具,许多用户在移动设备上快速获取信息并不方便,为此,我们推出了一款消息服务,每天在榜单更新结束后,精选当日排名靠前的产品,</view>
-    </view>
-  </view>
-</template>
-
-<script setup>
-import { ref } from 'vue';
-
-// 假设这是从后端获取的数据
-const subscribeData = ref([
-  {
-    time: '7天',
-    price: '1.0元',
-    action: '购买'
-  },
-  {
-    time: '30天',
-    price: '1.0元',
-    action: '购买'
-  },
-  {
-    time: '90天',
-    price: '1.0元',
-    action: '购买'
-  }
-]);
-
-// const subscribeColumns = ref([
-//   {
-//     title: '订阅时间',
-//     key: 'time'
-//   },
-//   {
-//     title: '价格',
-//     key: 'price'
-//   },
-//   {
-//     title: '操作',
-//     key: 'action',
-//     render: (item) => (
-//       <view class="action-button">{item.action}</view>
-//     )
-//   }
-// ]);
-</script>
-
-<style lang="scss" scoped>
-.bg-box {
-  background-color: #f5f5f5;
-}
-
-.bg-item {
-  background-color: #FFFFFF;
-  padding: 20rpx 24rpx;
-}
-
-.placehold {
-  height: 24rpx;
-  width: 100%;
-}
-
-.mt-24 {
-  margin-top: 24rpx;
-}
-
-.title {
-  font-size: 29.17rpx;
-  font-weight: 400;
-  letter-spacing: 0rpx;
-  line-height: 40.02rpx;
-  color: #000000;
-  text-align: left;
-  vertical-align: top;
-}
-
-.description {
-  font-size: 25rpx;
-  font-weight: 400;
-  letter-spacing: 0rpx;
-  line-height: 34.3rpx;
-  color: #999999;
-  text-align: left;
-  vertical-align: top;
-}
-
-.price-table {
-  border: 1rpx solid #E5E5E5;
-  padding: 0rpx 16rpx;
-
-  .price-item {
-    display: flex;
-  }
-
-  .price-border {
-    border-bottom: 1rpx solid #E5E5E5;
-  }
-
-  .price-border:last-child {
-    border-bottom: none;
-  }
-
-  .item {
-    flex: 1;
-    display: flex;
-    justify-content: center;
-    align-content: center;
-    padding: 16rpx;
-  }
-
-  .price {
-    color: #FF6744;
-  }
-
-  .buy-btn {
-    padding: 4rpx 24rpx;
-    background-color: #FFD800;
-    border-radius: 20rpx;
-
-    font-size: 24rpx;
-    font-weight: 400;
-    letter-spacing: 0rpx;
-    line-height: 34.75rpx;
-    color: #000000;
-    text-align: left;
-    vertical-align: top;
-  }
-}
-</style>

+ 77 - 15
src/pages/login.vue

@@ -2,18 +2,16 @@
   <view class="login-container">
     <view class="login-avator"></view>
     <view class="login-nickname">
-      <!-- <input v-model="loginFormData.nickname" placeholder="请输入昵称" /> -->
-      <input placeholder="请输入昵称" />
+      <input v-model="loginFormData.nickname" placeholder="请输入昵称" />
     </view>
 
     <view style="display: none">
-      <!-- <uni-forms ref="loginForm" :modelValue="loginFormData" :rules="loginFormRules"
-						err-show-type="modal">
-						<uni-forms-item name="nickname">
-							<uni-easyinput type="text" v-model="loginFormData.nickname" placeholder="请输入昵称"
-								:clearable="false" :inputBorder="false" />
-						</uni-forms-item>
-					</uni-forms> -->
+      <uni-forms ref="loginForm" :modelValue="loginFormData" :rules="loginFormRules" err-show-type="modal">
+        <uni-forms-item name="nickname">
+          <uni-easyinput type="text" v-model="loginFormData.nickname" placeholder="请输入昵称" :clearable="false"
+            :inputBorder="false" />
+        </uni-forms-item>
+      </uni-forms>
     </view>
 
     <view class="login-btn" @click="login">登录/注册</view>
@@ -22,12 +20,76 @@
 
 <script setup>
 import { ref } from 'vue';
-
-const nickname = ref('');
-
-const handleLogin = () => {
-  // 处理登录逻辑
-  console.log('昵称:', nickname.value);
+import { userApi } from "@/apis/apis.js";
+import router from "@/common/router.js";
+import cacheKey from "@/common/cache.js";
+
+// 组件
+/**
+ * 登录表单
+ */
+const loginForm = ref(null);
+
+// 属性
+/**
+ * 登录表单数据
+ */
+const loginFormData = ref({
+  nickname: null,
+});
+
+/**
+ * 登录表单校验规则
+ */
+const loginFormRules = ref({
+  nickname: {
+    rules: [
+      {
+        required: true,
+        errorMessage: "昵称不能为空",
+      },
+      {
+        maxLength: 30,
+        errorMessage: "昵称不能超过{maxLength}个字符",
+      },
+    ],
+  },
+});
+
+// 方法
+const login = async () => {
+  try {
+    let formData = await loginForm.value.validate();
+    uni.showLoading({
+      title: "登录中...",
+    });
+    // 获取供应商
+    let providerResult = await uni.getProvider({ service: "oauth" });
+    // 获取登录code
+    let loginResult = await uni.login({ provider: providerResult.provider[0] });
+    // 登录
+    let token = await userApi.login({
+      code: loginResult.code,
+      nickname: formData.nickname
+    });
+
+    // 保存用户信息(token)
+    uni.setStorageSync(cacheKey.TOKEN, token);
+
+    uni.showToast({
+      title: "登录成功",
+      icon: "success",
+      duration: 1000,
+      success: () => {
+        // 登录结束返回主页
+        uni.switchTab({
+          url: router.SUBSCRIPTION_SOURCE_MARKET_PAGE,
+        });
+      },
+    });
+  } finally {
+    uni.hideLoading();
+  }
 };
 </script>
 

+ 0 - 73
src/pages/market.vue

@@ -1,73 +0,0 @@
-<template>
-  <view>
-    <button @click="order">下单支付啦啦</button>
-    <!-- 订阅列表 -->
-    <uni-list>
-      <uni-list-item v-for="(item, index) in productList" :title="item.title" :note="item.description" rightText="详情"
-        link to="/pages/detail/productHuntDetail" />
-    </uni-list>
-  </view>
-</template>
-
-<script setup>
-import { ref } from 'vue';
-
-// 假设这是从后端获取的数据
-const productList = ref([
-  {
-    title: 'Product Hunt每日榜单',
-    description: '通过微信渠道快速推送 Product Hunt 前一天的 Top30 产品的中文信息摘要',
-    price: 3.99
-  },
-  {
-    title: 'Product Hunt每日榜单',
-    description: '通过微信渠道快速推送 Product Hunt 前一天的 Top30 产品的中文信息摘要',
-    price: 3.99
-  },
-  {
-    title: 'Product Hunt每日榜单',
-    description: '通过微信渠道快速推送 Product Hunt 前一天的 Top30 产品的中文信息摘要',
-    price: 3.99
-  },
-]);
-
-const order = async () => {
-  console.log("下单啦啦啦");
-  // 获取供应商
-  let providerResult = await uni.getProvider({ service: "oauth" });
-  uni.request({
-    url: "http://localhost:8080/test/t2",
-    method: "get",
-    success: ret => {
-      let res = ret.data;
-      console.log(res);
-      uni.requestPayment({
-        provider: providerResult.provider[0],
-        timeStamp: res.timeStamp,
-        nonceStr: res.nonceStr,
-        package: res.packageValue,
-        signType: res.signType,
-        paySign: res.paySign,
-        success: function (res) {
-          console.log('success:' + JSON.stringify(res));
-        },
-        fail: function (err) {
-          console.log('fail:' + JSON.stringify(err));
-        }
-      });
-    },
-    fail: err => {
-      console.log("一场 啦啦啦", err);
-      // reject(err);
-    },
-    complete: () => {
-      // if (loading) {
-      //   uni.hideLoading();
-      // }
-    }
-  })
-}
-
-</script>
-
-<style lang="scss" scoped></style>

+ 197 - 0
src/pages/subscriptionSourceDetail.vue

@@ -0,0 +1,197 @@
+<template>
+  <view class="bg-box">
+
+    <view class="placehold"></view>
+
+    <!-- 标题部分 -->
+    <view class="bg-item">
+      <view class="title">{{ subscriptionSource.title }}</view>
+      <view class="description">{{ subscriptionSource.subTitle }}</view>
+    </view>
+
+    <!-- 推送部分 -->
+    <view class="mt-24 bg-item">
+      <view class="title">推送</view>
+      <view class="description">推送渠道:{{ subscriptionSource.pushChannel }}</view>
+      <view class="description">推送频率:{{ subscriptionSource.pushFrequency }}</view>
+    </view>
+
+    <!-- 订阅时间部分 -->
+    <view class="mt-24 bg-item">
+      <view class="title">订阅</view>
+      <view class="price-table">
+        <view class="price-item price-border">
+          <view class="item">订阅时长</view>
+          <view class="item">价格</view>
+          <view class="item"></view>
+        </view>
+        <view class="price-item price-border" v-for="(item, index) in subscriptionSource.subscriptionPlans"
+          :key="item.id">
+          <view class="item">{{ item.subscriptionDuration }}天</view>
+          <view class="item price">{{ item.subscriptionPrice }}元</view>
+          <view class="item">
+            <view class="buy-btn" @click="createOrder(item.id)">购买</view>
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <!-- 详情部分 -->
+    <view class="mt-24 bg-item">
+      <view class="title">详情</view>
+      <view class="description">{{ subscriptionSource.description }}</view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { onLoad, onPullDownRefresh } from '@dcloudio/uni-app';
+import { subscriptionApi, orderApi } from '@/apis/apis.js';
+
+// 属性
+/**
+ * 订阅源ID
+ */
+const subscriptionSourceId = ref(null);
+
+/**
+ * 订阅源
+ */
+const subscriptionSource = ref({});
+
+// 方法
+/**
+ * 加载数据
+ */
+const loadData = async () => {
+  subscriptionSource.value = await subscriptionApi.querySubscriptionSourceDetail({
+    id: subscriptionSourceId.value
+  });
+  uni.setNavigationBarTitle({
+    title: subscriptionSource.value.title
+  });
+};
+
+/**
+ * 创建订单
+ */
+const createOrder = async (id) => {
+  // 获取支付参数
+  let res = await orderApi.createOrder({ subscriptionPlanId: id });
+  // 获取供应商
+  let providerResult = await uni.getProvider({ service: "oauth" });
+  // 调起微信支付
+  uni.requestPayment({
+    provider: providerResult.provider[0],
+    timeStamp: res.timeStamp,
+    nonceStr: res.nonceStr,
+    package: res.packageValue,
+    signType: res.signType,
+    paySign: res.paySign,
+    success: function (res) {
+      console.log('支付成功:' + JSON.stringify(res));
+    },
+    fail: function (err) {
+      console.log('支付失败:' + JSON.stringify(err));
+    }
+  });
+}
+
+// 生命周期
+onLoad(async (e) => {
+  if (e.id) {
+    subscriptionSourceId.value = e.id;
+    // 加载数据
+    loadData();
+  }
+});
+
+onPullDownRefresh(() => {
+  loadData();
+  uni.stopPullDownRefresh();
+});
+</script>
+
+<style lang="scss" scoped>
+.bg-box {
+  background-color: #f5f5f5;
+}
+
+.bg-item {
+  background-color: #FFFFFF;
+  padding: 20rpx 24rpx;
+}
+
+.placehold {
+  height: 24rpx;
+  width: 100%;
+}
+
+.mt-24 {
+  margin-top: 24rpx;
+}
+
+.title {
+  font-size: 29.17rpx;
+  font-weight: 400;
+  letter-spacing: 0rpx;
+  line-height: 40.02rpx;
+  color: #000000;
+  text-align: left;
+  vertical-align: top;
+}
+
+.description {
+  font-size: 25rpx;
+  font-weight: 400;
+  letter-spacing: 0rpx;
+  line-height: 34.3rpx;
+  color: #999999;
+  text-align: left;
+  vertical-align: top;
+}
+
+.price-table {
+  border: 1rpx solid #E5E5E5;
+  padding: 0rpx 16rpx;
+
+  .price-item {
+    display: flex;
+  }
+
+  .price-border {
+    border-bottom: 1rpx solid #E5E5E5;
+  }
+
+  .price-border:last-child {
+    border-bottom: none;
+  }
+
+  .item {
+    flex: 1;
+    display: flex;
+    justify-content: center;
+    align-content: center;
+    padding: 16rpx;
+  }
+
+  .price {
+    color: #FF6744;
+  }
+
+  .buy-btn {
+    padding: 4rpx 24rpx;
+    background-color: #FFD800;
+    border-radius: 20rpx;
+
+    font-size: 24rpx;
+    font-weight: 400;
+    letter-spacing: 0rpx;
+    line-height: 34.75rpx;
+    color: #000000;
+    text-align: left;
+    vertical-align: top;
+  }
+}
+</style>

+ 65 - 0
src/pages/subscriptionSourceMarket.vue

@@ -0,0 +1,65 @@
+<template>
+  <view>
+    <!-- 订阅列表 -->
+    <uni-list>
+      <uni-list-item v-for="(item, index) in subscriptionSourceList" :key="item.id" :title="item.title"
+        :note="item.subTitle" :rightText="item.paidOption == 'PAID' ? '付费' : '免费'" link :clickable="true"
+        @click="goSubscriptionSourceDetail(item)" />
+    </uni-list>
+    <uni-load-more status="no-more" v-if="!subscriptionSourceList || subscriptionSourceList.length == 0" />
+  </view>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { onPullDownRefresh, onShow } from "@dcloudio/uni-app";
+import { subscriptionApi } from '@/apis/apis.js';
+import router from '@/common/router.js';
+
+// 属性
+/**
+ * 订阅源列表
+ */
+const subscriptionSourceList = ref([]);
+
+// 方法
+/**
+ * 加载数据
+ */
+const loadData = async () => {
+  subscriptionSourceList.value = [];
+  // 已登录
+  try {
+    uni.showLoading({
+      title: '加载中',
+      mask: true
+    });
+    let value = await subscriptionApi.querySubscriptionSource();
+    subscriptionSourceList.value = value;
+  } finally {
+    uni.hideLoading();
+  }
+};
+
+/**
+ * 跳转到订阅源详情页面
+ */
+const goSubscriptionSourceDetail = (dataObj) => {
+  uni.navigateTo({
+    url: router.SUBSCRIPTION_SOURCE_DETAIL_PAGE + `?id=${dataObj.id}`
+  });
+};
+
+
+// 生命周期
+onShow(() => {
+  loadData();
+});
+
+onPullDownRefresh(() => {
+  loadData();
+  uni.stopPullDownRefresh();
+});
+</script>
+
+<style lang="scss" scoped></style>

+ 44 - 22
src/pages/subscribeList.vue → src/pages/subscriptionUserConfig.vue

@@ -2,11 +2,11 @@
   <view>
     <!-- 已订阅列表 -->
     <uni-list>
-      <uni-list-item v-for="(item1, index) in productList" :key="index">
+      <uni-list-item v-for="(item, index) in productList" :key="item.id">
         <template v-slot:body>
           <view class="item-box">
-            <view class="title">{{ item1.title }}</view>
-            <view class="description">{{ item1.description }}</view>
+            <view class="title">{{ item.title }}</view>
+            <view class="description">{{ item.subTitle }}</view>
             <view class="func-box">
               <view class="date">剩余有效期:7天</view>
               <view class="switch-text">
@@ -23,27 +23,49 @@
 
 <script setup>
 import { ref } from 'vue';
+import { onPullDownRefresh, onShow } from "@dcloudio/uni-app";
+import { subscriptionApi } from '@/apis/apis.js';
+import router from '@/common/router.js';
 
-// 假设这是从后端获取的数据
-const productList = ref([
-  {
-    title: 'Product Hunt每日榜单',
-    description: '通过微信渠道快速推送 Product Hunt 前一天的 Top30 产品的中文信息摘要',
-    price: 3.99
-  },
-  {
-    title: 'Product Hunt每日榜单',
-    description: '通过微信渠道快速推送 Product Hunt 前一天的 Top30 产品的中文信息摘要',
-    price: 3.99
-  },
-  {
-    title: 'Product Hunt每日榜单',
-    description: '通过微信渠道快速推送 Product Hunt 前一天的 Top30 产品的中文信息摘要',
-    price: 3.99
-  },
-  // ... 更多产品数据
-]);
+// 属性
+const subscriptionUserConfigList = ref([]);
 
+
+// 方法
+/**
+ * 加载数据
+ */
+const loadData = async () => {
+  subscriptionUserConfigList.value = [];
+  try {
+    uni.showLoading({
+      title: '加载中',
+      mask: true
+    });
+    subscriptionUserConfigList.value = await subscriptionApi.querySubscriptionUserConfig();
+  } finally {
+    uni.hideLoading();
+  }
+};
+
+/**
+ * 跳转到订阅源详情页面
+ */
+const goSubscriptionSourceDetail = (dataObj) => {
+  uni.navigateTo({
+    url: router.SUBSCRIPTION_SOURCE_DETAIL_PAGE + `?id=${dataObj.id}`
+  });
+};
+
+// 生命周期
+onShow(() => {
+  loadData();
+});
+
+onPullDownRefresh(() => {
+  loadData();
+  uni.stopPullDownRefresh();
+});
 </script>
 
 <style lang="scss" scoped>

+ 73 - 7
src/pages/userInfo.vue → src/pages/userCenter.vue

@@ -10,18 +10,19 @@
             <view class="avatar-box">
               <view class="avatar">
                 <view>
-                  <Avatar :username="nickname" color="#FFFFFF" backgroundColor="#FFD800"></Avatar>
+                  <Avatar :username="userInfo.nickname" color="#FFFFFF" backgroundColor="#FFD800"></Avatar>
                 </view>
-                <span class="nickname">{{ nickname }}</span>
+                <span class="nickname">{{ userInfo.nickname }}</span>
               </view>
-              <view class="avatar-func">修改</view>
+              <view class="avatar-func" v-if="loginStatus">修改</view>
+              <view class="avatar-func" v-else @click="login()">登录</view>
             </view>
           </template>
         </uni-list-item>
       </uni-list>
     </view>
 
-    <view class="mt24">
+    <view class="mt24" v-if="loginStatus">
       <uni-list :border="true">
         <uni-list-item title="订单记录" :showArrow="true" :showExtraIcon="true"
           :extraIcon="{ color: '#000000', size: 22, type: 'list' }">
@@ -40,10 +41,10 @@
       </uni-list>
     </view>
 
-    <view class="mt24">
+    <view class="mt24" v-if="loginStatus">
       <uni-list :border="true">
         <uni-list-item title="退出登录" :showArrow="true" :showExtraIcon="true"
-          :extraIcon="{ color: 'red', size: 22, type: 'closeempty' }">
+          :extraIcon="{ color: 'red', size: 22, type: 'closeempty' }" :clickable="true" @click="logout()">
         </uni-list-item>
       </uni-list>
     </view>
@@ -53,8 +54,73 @@
 
 <script setup>
 import { ref } from 'vue';
+import { onShow } from '@dcloudio/uni-app';
+import cacheKey from "@/common/cache.js";
+import router from "@/common/router.js";
+import { userApi } from "@/apis/apis.js";
 import Avatar from 'vue-avatar/src/Avatar.vue';
-const nickname = ref('蜗牛');
+
+
+// 属性
+/**
+ * 登录状态:false-未登录,true-已登录
+ */
+const loginStatus = ref(false);
+
+/**
+ * 用户信息
+ */
+const userInfo = ref({
+  nickname: ''
+});
+
+// 方法
+/**
+ * 登录
+ */
+const login = () => {
+  uni.navigateTo({
+    url: router.LOGIN_PAGE,
+  });
+};
+
+/**
+ * 注销登录
+ */
+const logout = () => {
+  uni.showModal({
+    title: "注销提示",
+    content: "确认退出登录?",
+    success: function (res) {
+      // 如果取消,则不执行后续代码
+      if (res.cancel) {
+        return;
+      }
+      uni.removeStorageSync(cacheKey.TOKEN);
+      uni.reLaunch({
+        url: router.SUBSCRIPTION_SOURCE_MARKET_PAGE,
+      });
+    }
+  });
+};
+
+/**
+ * 获取用户信息
+ */
+const fetchUserInfo = async () => {
+  userInfo.value = await userApi.queryUserInfo();
+};
+
+// 生命周期
+onShow(() => {
+  // 根据登录凭据判断登录状态
+  loginStatus.value = uni.getStorageSync(cacheKey.TOKEN) ? true : false;
+  if (loginStatus.value) {
+    fetchUserInfo();
+  } else {
+    userInfo.value.nickname = '游客';
+  }
+});
 </script>
 
 <style lang="scss" scoped>

+ 93 - 0
src/utils/request.js

@@ -0,0 +1,93 @@
+import router from '@/common/router.js';
+import cacheKey from '@/common/cache.js';
+
+/**
+ * 通用网络请求工具
+ * @param {*} config 
+ * @returns 
+ */
+export default function request(config = {}) {
+
+	// 入参
+	let {
+		url,
+		data = {},
+		method = "GET",
+		header = {},
+		loading = false,
+		loadingText = "请求中..."
+	} = config
+
+	// 拼接url
+	url = process.env.BASE_API_URL + url;
+
+	// 添加token
+	let token = uni.getStorageSync(cacheKey.TOKEN);
+	if (token) {
+		header['Authorization'] = token;
+	}
+
+	// 是否显示loading
+	if (loading) {
+		uni.showLoading({
+			title: loadingText,
+			mask: true
+		});
+	}
+
+	return new Promise((resolve, reject) => {
+		uni.request({
+			url,
+			data,
+			method,
+			header,
+			withCredentials: true,
+			success: res => {
+				// 登录状态判断
+				if (res.statusCode === 401) {
+					// 清除token
+					uni.removeStorageSync(cacheKey.TOKEN);
+					// 跳转登录页面
+					uni.showModal({
+						title: "登录提示",
+						content: "登录凭据已失效,请重新登录",
+						showCancel: false,
+						success: function (res) {
+							if (res.confirm) {
+								uni.navigateTo({
+									url: router.LOGIN_PAGE
+								});
+							}
+						}
+					});
+					reject(res.data);
+					return;
+				}
+				if (res.data.code === '0') {
+					resolve(res.data.data);
+				} else if (res.data.code === '999') {
+					uni.showModal({
+						title: "错误提示",
+						content: res.data.msg,
+						showCancel: false
+					});
+					reject(res.data);
+				} else {
+					uni.showToast({
+						title: res.data.msg,
+						icon: "none"
+					});
+					reject(res.data);
+				}
+			},
+			fail: err => {
+				reject(err);
+			},
+			complete: () => {
+				if (loading) {
+					uni.hideLoading();
+				}
+			}
+		})
+	})
+}