Sfoglia il codice sorgente

🎨 修改细节问题

快乐的梦鱼 1 settimana fa
parent
commit
9aa93a3bb2

+ 87 - 0
README.md

@@ -0,0 +1,87 @@
+# 闽南文化小程序
+
+构建"数字闽南文化生态圈",打造集文化保护、学术研究、公众教育、产业应用于一体的综合性数字平台。通过建立"1+3+N"服务体系(1个核心数据库、3大应用终端、N个特色场景),形成覆盖全市的文化数字服务网络。
+
+本项目为闽南文化生态保护区(厦门市)的小程序项目源代码。
+
+## 技术栈
+
+### 前端核心技术
+
+- **框架**: Uniapp+Vue 3
+- **状态管理**: Pinia 3.0
+- **语言**: TypeScript
+- **样式预处理器**: Sass
+
+## 项目结构
+
+### 核心依赖
+
+- `pinia`: 状态管理库
+- `naeasy-ui-uniapp`: [NaEasy UI 组件库](./src/components/README.md)
+- `async-validator`: 表单验证
+- `sp-editor`: 富文本编辑器
+- `@imengyu/js-request-transform`: API请求转换工具
+
+### 目录组织
+
+```
+src/
+├── api/             # API请求模块
+│   ├── auth/        # 登录相关API
+│   ├── fusion/      # 文旅融合内容API
+│   ├── inheritor/   # 文化传承相关API
+│   ├── inhert/      # 村落相关API
+│   ├── introduction/# 闽南文化简介相关API
+│   ├── news/        # 资讯相关API
+│   ├── running/     # 文物跑相关API
+│   ├── user/        # 用户相关API
+│   ├── BaseAppServerRequestModule.ts  # 基础请求配置模块
+│   ├── RequestModules.ts             # 请求核心模块
+├── common/          # 通用功能
+│   ├── components/  # 通用组件
+│   ├── composeabe/  # 组合式函数
+│   ├── config/      # 配置文件
+│   ├── scss/        # 全局样式定义
+├── components/      # NaEasy UI 组件库
+├── pages/           # 页面
+│   ├── article/     # 文章相关页面
+│   ├── home/        # 首页相关页面
+│   ├── inhert/      # 传承相关页面
+│   ├── introduction/ # 闽南文化简介相关页面
+│   ├── parts/       # 页面模块组件拆分
+│   ├── travel/      # 文旅融合相关页面
+│   ├── user/        # 用户相关页面,登录,个人信息,投稿
+│   ├── video/       # 视频相关页面
+├── store/           # 状态管理模块
+│   ├── auth/        # 登录状态管理
+├── App.vue          # 应用入口
+├── main.ts          # 主入口文件
+├── manifest.json    # 应用配置
+├── pages.json       # 页面配置
+```
+
+## 开发与构建
+
+### 开发命令
+
+```bash
+# 启动H5开发服务器
+npm run dev:h5
+# 启动微信小程序开发服务器
+npm run dev:mp-weixin
+```
+
+### 构建命令
+
+```bash
+# 构建微信小程序版本
+npm run build:mp-weixin
+```
+
+### 类型检查
+
+```bash
+# 执行TypeScript类型检查
+npm run type-check
+```

+ 2 - 1
src/App.vue

@@ -4,6 +4,7 @@ import { useAuthStore } from './store/auth'
 import { configTheme } from './components/theme/ThemeDefine';
 import { RequestApiConfig } from '@imengyu/imengyu-utils';
 import ApiCofig from './common/config/ApiCofig';
+import { isDev } from './common/config/AppCofig';
 
 const authStore = useAuthStore();
 
@@ -26,7 +27,7 @@ onLaunch(() => {
 
 RequestApiConfig.setConfig({
   ...RequestApiConfig.getConfig(),
-  BaseUrl: ApiCofig.serverProd,
+  BaseUrl: isDev ? ApiCofig.server.Dev : ApiCofig.server.Prod,
 })
 
 configTheme(false, (theme, darkTheme) => {

+ 2 - 0
src/api/CommonContent.ts

@@ -532,6 +532,8 @@ export class CommonContentApi extends AppServerRequestModule<DataModel> {
         url = newUrl;
         data = newReq;
       }
+      console.log(url);
+      
       uni.uploadFile({
         url: url,
         name,

+ 4 - 4
src/common/components/tabs/tabbar.vue

@@ -33,16 +33,16 @@ const props = defineProps({
 function changeTab(newVal: number) {
   switch(newVal) {
     case 0:
-      switchTab('/pages/home', 0);
+      switchTab('/pages/home/index', 0);
       break;
     case 1:
-      switchTab('/pages/discover', 1);
+      switchTab('/pages/article/index', 1);
       break;
     case 2:
-      switchTab('/pages/inhert', 2);
+      switchTab('/pages/inhert/index', 2);
       break;
     case 3:
-      switchTab('/pages/travel', 3);
+      switchTab('/pages/travel/index', 3);
       break;
     case 4:
       switchTab('/pages/user/index', 4);

+ 2 - 2
src/manifest.json

@@ -1,7 +1,7 @@
 {
-	"name": "mingnan",
+	"name": "minnan",
 	"appid": "__UNI__971AA7D",
-	"description": "",
+	"description": "闽南文化生态保护区(厦门市)",
 	"versionName": "1.0.0",
 	"versionCode": "100",
 	"transformPx": false,

+ 9 - 9
src/pages.json

@@ -1,7 +1,7 @@
 {
   "pages": [
     {
-      "path": "pages/home",
+      "path": "pages/home/index",
       "style": {
         "navigationBarTitleText": "闽南文化生态保护-首页",
         "navigationStyle": "custom"
@@ -87,7 +87,7 @@
       }
     },
     {
-      "path": "pages/travel",
+      "path": "pages/travel/index",
       "style": {
         "navigationBarTitleText": "闽南文化生态保护-文旅",
         "navigationStyle": "custom"
@@ -110,19 +110,19 @@
     {
       "path": "pages/travel/fashion/list",
       "style": {
-        "navigationBarTitleText": "闽南歌曲",
+        "navigationBarTitleText": "闽南时尚",
         "enablePullDownRefresh": true
       }
     },
     {
-      "path": "pages/discover",
+      "path": "pages/article/index",
       "style": {
         "navigationBarTitleText": "闽南文化生态保护-发现",
         "navigationStyle": "custom"
       }
     },
     {
-      "path": "pages/inhert",
+      "path": "pages/inhert/index",
       "style": {
         "navigationBarTitleText": "闽南文化生态保护-传承",
         "navigationStyle": "custom"
@@ -340,19 +340,19 @@
     "custom": true,
     "list": [
       {
-        "pagePath": "pages/home",
+        "pagePath": "pages/home/index",
         "text": "首页"
       },
       {
-        "pagePath": "pages/discover",
+        "pagePath": "pages/article/index",
         "text": "资讯"
       },
       {
-        "pagePath": "pages/inhert",
+        "pagePath": "pages/inhert/index",
         "text": "传承"
       },
       {
-        "pagePath": "pages/travel",
+        "pagePath": "pages/travel/index",
         "text": "文旅"
       },
       {

+ 2 - 2
src/pages/discover.vue

@@ -18,10 +18,10 @@
 </template>
 
 <script setup lang="ts">
+import { onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app';
 import Tabbar from '@/common/components/tabs/Tabbar.vue';
-import News from './introduction/news.vue';
+import News from '../introduction/news.vue';
 import FlexCol from '@/components/layout/FlexCol.vue';
-import { onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app';
 import NavBar from '@/components/nav/NavBar.vue';
 import StatusBarSpace from '@/components/layout/space/StatusBarSpace.vue';
 import Image from '@/components/basic/Image.vue';

+ 3 - 3
src/pages/home.vue

@@ -192,6 +192,7 @@ import { onShareTimeline, onShareAppMessage } from '@dcloudio/uni-app';
 import { navTo } from '@/components/utils/PageAction';
 import { useSimpleDataLoader } from '@/common/composeabe/SimpleDataLoader';
 import { useSimpleListAudioPlayer } from '@/common/composeabe/SimpleAudioPlayer';
+import { navHomePageMiniCommonListGo } from '@/pages/article/common/CommonContent';
 import CommonContent, { GetContentListParams } from '@/api/CommonContent';
 import UnmoveableContent from '@/api/inheritor/UnmoveableContent';
 import SeminarContent from '@/api/inheritor/SeminarContent';
@@ -200,15 +201,14 @@ import AppCofig from '@/common/config/AppCofig';
 import VillageApi from '@/api/inhert/VillageApi';
 import ScenicSpotContent from '@/api/fusion/ScenicSpotContent';
 import IndexContent from '@/api/introduction/IndexContent';
-import StatsText, { type StatsTextItem } from './parts/StatsText.vue';
+import StatsText, { type StatsTextItem } from '../parts/StatsText.vue';
 import HomeTitle from '@/pages/parts/HomeTitle.vue'; 
 import Tabbar from '@/common/components/tabs/Tabbar.vue';
 import Box1AudioPlay from '@/pages/parts/Box1AudioPlay.vue';
 import SimplePageContentLoader from "@/common/components/SimplePageContentLoader.vue";
 import HorizontalScrollText from '@/components/typography/HorizontalScrollText.vue';
 import Image from '@/components/basic/Image.vue';
-import HomeButton from './parts/HomeButton.vue';
-import { navHomePageMiniCommonListGo } from './article/common/CommonContent';
+import HomeButton from '../parts/HomeButton.vue';
 
 const mapCtx = uni.createMapContext('map');
 const mapTab = ref(1);

+ 1 - 1
src/pages/inhert.vue

@@ -20,7 +20,7 @@
 <script setup lang="ts">
 import Tabbar from '@/common/components/tabs/Tabbar.vue';
 import { onShareTimeline, onShareAppMessage } from '@dcloudio/uni-app';
-import Inhert from './introduction/inhert.vue';
+import Inhert from '../introduction/inhert.vue';
 import FlexCol from '@/components/layout/FlexCol.vue';
 import StatusBarSpace from '@/components/layout/space/StatusBarSpace.vue';
 import NavBar from '@/components/nav/NavBar.vue';

+ 1 - 1
src/pages/parts/RoundTags.vue

@@ -7,7 +7,7 @@
       :key="k" 
       class="bg-place mr-2 mb-2"
       :style="{
-        maxWidth: '160rpx',
+        maxWidth: '220rpx',
         overflow: 'hidden',
         textOverflow: 'ellipsis',
         whiteSpace: 'nowrap',

+ 2 - 2
src/pages/travel/fashion/list.vue

@@ -6,7 +6,6 @@
     detailsPage="/pages/video/details"
     :dropDownNames="dropdownNames"
     :detailsParams="detailsParams"
-    :tabsScrollable="true"
     :tabs="[
       /*{
         id: 191,
@@ -25,7 +24,7 @@
       {
         id: 315,
         text: '闽南语原创歌曲',
-        width: 200,
+        //width: 200,
       },
       /*{
         id: -100,
@@ -84,6 +83,7 @@ async function loadData(
   res.list.forEach((p) => {
     p.desc = `来源:${p.from}\n` + (p.desc || '');
     p.bottomTags = [
+      model.name, 
       p.levelText, 
       p.batchText,
       p.ichTypeText,

+ 2 - 2
src/pages/travel.vue

@@ -18,13 +18,13 @@
 </template>
 
 <script setup lang="ts">
-import Tabbar from '@/common/components/tabs/Tabbar.vue';
 import { onShareTimeline, onShareAppMessage } from '@dcloudio/uni-app';
+import Tabbar from '@/common/components/tabs/Tabbar.vue';
 import FlexCol from '@/components/layout/FlexCol.vue';
 import StatusBarSpace from '@/components/layout/space/StatusBarSpace.vue';
 import NavBar from '@/components/nav/NavBar.vue';
 import Image from '@/components/basic/Image.vue';
-import Travel from './introduction/travel.vue';
+import Travel from '../introduction/travel.vue';
 import Height from '@/components/layout/space/Height.vue';
 
 onShareTimeline(() => {

+ 59 - 58
src/pages/user/profile/index.vue

@@ -1,46 +1,50 @@
 <template>
-  <FlexCol height="100vh" :padding="30" backgroundColor="#f6f2e7">
-    <Form 
-      ref="formRef"
-      :model="formModel"
-      :rules="rules" 
-      validateTrigger="submit"
-      labelAlign="right"
-      :labelWidth="140"
-    >
-      <!-- 头像 -->
-      <view class="avatar-section">
-        <button open-type="chooseAvatar" class="remove-button-style" @chooseavatar="handleChooseAvatar">
-          <view class="avatar-container">
-            <image 
-              :src="formModel.avatar || DefaultAvatar" 
-              class="avatar-image" 
-              mode="aspectFill"
-            />
-            <text class="avatar-hint">点击可修改头像</text>
-          </view>
-        </button>
-      </view>
-
-      <Field name="nickname" label="昵称" placeholder="请输入昵称" type="nickname" required />
-      <Field name="bio" multiline label="个人简介" placeholder="输入个人简介" :inputStyle="{width: '240px'}" />
-    </Form>
-
-    <Height :height="40" />
-
-    <NaButton type="primary" :loading="loading" @click="submitForm" >
-      保存修改
-    </NaButton>
-    <Height :height="20" />
-    <NaButton type="primary" scheme="plain" @click="back()">
-      返回
-    </NaButton>
-  </FlexCol>
+  <CommonRoot>
+    <FlexCol height="100vh" :padding="30" backgroundColor="#f6f2e7">
+      <Form 
+        ref="formRef"
+        :model="formModel"
+        :rules="rules" 
+        validateTrigger="submit"
+        labelAlign="right"
+        :labelWidth="140"
+      >
+        <!-- 头像 -->
+        <view class="avatar-section">
+          <button open-type="chooseAvatar" class="remove-button-style" @chooseavatar="handleChooseAvatar">
+            <view class="avatar-container">
+              <image 
+                :src="formModel.avatar || DefaultAvatar" 
+                class="avatar-image" 
+                mode="aspectFill"
+              />
+              <text class="avatar-hint">点击可修改头像</text>
+            </view>
+          </button>
+        </view>
+
+        <Field name="nickname" label="昵称" placeholder="请输入昵称" type="nickname" required />
+        <Field name="bio" multiline label="个人简介" placeholder="输入个人简介" :inputStyle="{width: '240px'}" />
+      </Form>
+
+      <Height :height="40" />
+
+      <NaButton type="primary" :loading="loading" @click="submitForm" >
+        保存修改
+      </NaButton>
+      <Height :height="20" />
+      <NaButton type="primary" scheme="plain" @click="back()">
+        返回
+      </NaButton>
+    </FlexCol>
+  </CommonRoot>
 </template>
 
 <script setup lang="ts">
 import { ref, onMounted } from 'vue';
 import { useAuthStore } from '@/store/auth';
+import { back } from '@/components/utils/PageAction';
+import type { Rules } from 'async-validator';
 import UserApi from '@/api/auth/UserApi';
 import CommonContent from '@/api/CommonContent';
 import Form from '@/components/form/Form.vue';
@@ -48,10 +52,8 @@ import Field from '@/components/form/Field.vue';
 import FlexCol from '@/components/layout/FlexCol.vue';
 import NaButton from '@/components/basic/Button.vue';
 import Height from '@/components/layout/space/Height.vue';
-import type { Rules } from 'async-validator';
-import { back } from '@/components/utils/PageAction';
-import Dialog from '@/components/dialog/Dialog.vue';
-import Avatar from '@/components/display/Avatar.vue';
+import CommonRoot from '@/components/dialog/CommonRoot.vue';
+import { toast } from '@/components/dialog/CommonRoot';
 
 const DefaultAvatar = 'https://mncdn.wenlvti.net/app_static/minnan/images/home/UserHead.png';
 const authStore = useAuthStore();
@@ -75,21 +77,24 @@ const rules : Rules = {
   ],
 };
 
-
-
 const handleChooseAvatar = async (e: { detail: { avatarUrl: string } }) => {
   try {
+    uploading.value = true;
     const tempFilePath = e.detail.avatarUrl;
     // 上传图片
-    uploading.value = true;
     const uploadResult = await CommonContent.uploadFile(tempFilePath, 'image');
     // 更新头像并保存到服务器
     await updateAvatar(uploadResult.fullurl);  
+    toast({
+      content: '头像更换成功',
+      icon: 'success'
+    });
   } catch (error: any) {
+    console.error(error);
     if (error.errMsg !== 'chooseImage:fail cancel') {
-      uni.showToast({
-        title: '头像更换失败',
-        icon: 'none',
+      toast({
+        content: '头像更换失败。' + error?.errMsg,
+        icon: 'error',
         duration: 2000
       });
     }
@@ -107,10 +112,9 @@ const updateAvatar = async (avatarUrl: string) => {
       authStore.userInfo.avatar = avatarUrl;
       authStore.saveLoginState();
     }
-    uni.showToast({
-      title: '头像更新成功',
+    toast({
+      content: '头像更新成功',
       icon: 'success',
-      duration: 2000
     });
     
   } catch (error: any) {
@@ -119,7 +123,6 @@ const updateAvatar = async (avatarUrl: string) => {
 };
 
 onMounted(() => {
-  console.log(authStore.userInfo);
   if (authStore.userInfo) {
     formModel.value.avatar = authStore.userInfo.avatar || '';
     formModel.value.nickname = authStore.userInfo.nickname || '';
@@ -147,20 +150,18 @@ const submitForm = async () => {
       authStore.userInfo.avatar = formModel.value.avatar;
       authStore.userInfo.intro = formModel.value.bio;
     }
-    uni.showToast({
-      title: '个人信息更新成功',
+    toast({
+      content: '个人信息更新成功',
       icon: 'success',
-      duration: 2000
     });
     setTimeout(() => {
       uni.navigateBack();
     }, 2000);
     
   } catch (error: any) {
-    uni.showToast({
-      title: error?.message || '更新失败,请稍后重试',
-      icon: 'none',
-      duration: 2000
+    toast({
+      content: error?.message || '更新失败,请稍后重试',
+      icon: 'error',
     });
   } finally {
     loading.value = false;