快乐的梦鱼 преди 1 месец
родител
ревизия
930901ea92
променени са 78 файла, в които са добавени 1 реда и са изтрити 19737 реда
  1. 0 34
      .gitignore
  2. 0 3
      .vscode/extensions.json
  3. 1 33
      README.md
  4. 0 9
      config.json.example
  5. 0 1
      env.d.ts
  6. 0 13
      index.html
  7. 0 60
      nuxt.config.ts
  8. 0 14417
      package-lock.json
  9. 0 61
      package.json
  10. BIN
      public/favicon.ico
  11. 0 2
      public/robots.txt
  12. 0 23
      server/api/channel/nav.ts
  13. 0 22
      server/api/health.ts
  14. 0 48
      server/config/db.ts
  15. 0 130
      server/db/CommonModel.ts
  16. 0 96
      server/db/DB.ts
  17. 0 109
      server/db/DBUtils.ts
  18. 0 73
      server/db/Query.ts
  19. 0 1071
      server/db/QueryGenerator.ts
  20. 0 3
      server/tsconfig.json
  21. 0 27
      server/utils/response.ts
  22. 0 60
      src/App.vue
  23. 0 12
      src/api/NotConfigue.ts
  24. 0 207
      src/api/RequestModules.ts
  25. 0 15
      src/api/Utils.ts
  26. 0 68
      src/api/auth/UserApi.ts
  27. BIN
      src/assets/fonts/PingFang Regular.ttf
  28. BIN
      src/assets/fonts/STSongti-SC-Black.ttf
  29. BIN
      src/assets/fonts/STSongti-SC-Black.woff
  30. BIN
      src/assets/fonts/STSongti-SC-Black.woff2
  31. BIN
      src/assets/images/about-logo.png
  32. BIN
      src/assets/images/badge.png
  33. BIN
      src/assets/images/box-service.png
  34. BIN
      src/assets/images/button-active.png
  35. BIN
      src/assets/images/button-deactive.png
  36. BIN
      src/assets/images/footer-bg.png
  37. BIN
      src/assets/images/icon-contract.png
  38. BIN
      src/assets/images/icon-explore.png
  39. BIN
      src/assets/images/icon-join.png
  40. BIN
      src/assets/images/tab-active.png
  41. BIN
      src/assets/images/test-header-1.png
  42. BIN
      src/assets/images/test-header-2.png
  43. BIN
      src/assets/images/test-header-3.png
  44. BIN
      src/assets/images/test-header-4.png
  45. BIN
      src/assets/images/test-header-5.png
  46. BIN
      src/assets/images/test-header-6.png
  47. BIN
      src/assets/images/test-header-7.png
  48. BIN
      src/assets/images/test-header-8.png
  49. BIN
      src/assets/images/xuexi.png
  50. 0 8
      src/assets/scss/fix.scss
  51. 0 36
      src/assets/scss/fonts.scss
  52. 0 836
      src/assets/scss/main.scss
  53. 0 63
      src/assets/scss/scroll.scss
  54. 0 1
      src/common/ConstStrings.ts
  55. 0 98
      src/common/ConvertRgeistry.ts
  56. 0 8
      src/common/EventBus.ts
  57. 0 9
      src/common/config/ApiCofig.ts
  58. 0 11
      src/common/config/AppCofig.ts
  59. 0 55
      src/components/Footer.vue
  60. 0 64
      src/components/NavBar.vue
  61. 0 80
      src/components/content/SimplePageContentLoader.vue
  62. 0 9
      src/composeable/LoaderCommon.ts
  63. 0 103
      src/composeable/PageAction.ts
  64. 0 38
      src/composeable/PageQuerys.ts
  65. 0 109
      src/composeable/SimpleDataLoader.ts
  66. 0 232
      src/composeable/SimplePagerDataLoader.ts
  67. 0 44
      src/pages/404.vue
  68. 0 107
      src/pages/about.vue
  69. 0 54
      src/pages/channel/[id].vue
  70. 0 50
      src/pages/index.vue
  71. 0 4
      src/scripts/.gitignore
  72. 0 22
      src/scripts/UpdateScript/deprecate.html
  73. 0 524
      src/scripts/UpdateScript/index.mjs
  74. 0 66
      src/scripts/UpdateScript/postConfig.mjs
  75. 0 430
      src/scripts/UpdateScript/postUpdate.mjs
  76. 0 74
      src/stores/auth.ts
  77. 0 1
      test
  78. 0 4
      tsconfig.json

+ 0 - 34
.gitignore

@@ -1,34 +0,0 @@
-# Logs
-logs
-*.log
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-pnpm-debug.log*
-lerna-debug.log*
-/config.json
-
-node_modules
-.DS_Store
-dist
-dist-ssr
-coverage
-*.local
-
-.output
-.nuxt
-
-/cypress/videos/
-/cypress/screenshots/
-
-# Editor directories and files
-.vscode/*
-!.vscode/extensions.json
-.idea
-*.suo
-*.ntvs*
-*.njsproj
-*.sln
-*.sw?
-
-*.tsbuildinfo

+ 0 - 3
.vscode/extensions.json

@@ -1,3 +0,0 @@
-{
-  "recommendations": ["Vue.volar"]
-}

+ 1 - 33
README.md

@@ -1,33 +1 @@
-# mingnan-website
-
-This template should help get you started developing with Vue 3 in Vite.
-
-## Recommended IDE Setup
-
-[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
-
-## Type Support for `.vue` Imports in TS
-
-TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
-
-## Customize configuration
-
-See [Vite Configuration Reference](https://vite.dev/config/).
-
-## Project Setup
-
-```sh
-npm install
-```
-
-### Compile and Hot-Reload for Development
-
-```sh
-npm run dev
-```
-
-### Type-Check, Compile and Minify for Production
-
-```sh
-npm run build
-```
+# wenbao-website

+ 0 - 9
config.json.example

@@ -1,9 +0,0 @@
-{
-  "db": {
-    "host": "localhost",
-    "port": 3306,
-    "user": "root",
-    "password": "your_password",
-    "database": "wenbao"
-  }
-}

+ 0 - 1
env.d.ts

@@ -1 +0,0 @@
-/// <reference types="vite/client" />

+ 0 - 13
index.html

@@ -1,13 +0,0 @@
-<!DOCTYPE html>
-<html lang="">
-  <head>
-    <meta charset="UTF-8">
-    <link rel="icon" href="/favicon.ico">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>闽南文化生态保护区(厦门市)</title>
-  </head>
-  <body>
-    <div id="app"></div>
-    <script type="module" src="/src/main.ts"></script>
-  </body>
-</html>

+ 0 - 60
nuxt.config.ts

@@ -1,60 +0,0 @@
-import { defineNuxtConfig } from "nuxt/config";
-
-// https://nuxt.com/docs/api/configuration/nuxt-config
-export default defineNuxtConfig({
-  compatibilityDate: '2025-05-15',
-  app: {
-    head: {
-      title: '厦门市文化遗产保护中心',
-      viewport: 'width=device-width, initial-scale=1, maximum-scale=1',
-      htmlAttrs: {
-        lang: 'zh',
-      },
-      link: [
-        { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
-      ]
-    }
-  }, 
-  css: ['bootstrap/dist/css/bootstrap.min.css'],
-  devtools: { enabled: true },
-  srcDir: 'src/',
-  modules: ['@pinia/nuxt', '@ant-design-vue/nuxt', '@nuxt/icon'],
-  components: [
-    {
-      path: '~/components',
-      pathPrefix: false,
-      extensions: ['.vue'],
-    }
-  ],
-  icon: {
-    provider: 'iconify',
-    serverBundle: false,
-  },
-  build: {
-    transpile: [
-      '@imengyu/vue-scroll-rect',
-      '@imengyu/imengyu-utils',
-    ],
-  },
-  vite: {
-    build: {
-      minify: 'terser',
-      terserOptions: {
-        compress: {
-          drop_console: true,
-          drop_debugger: true,
-        },
-      },
-    },
-  },
-  routeRules: {
-    '/**': { swr: false, isr: false, headers: { 'cache-control': 'no-store, max-age=0' } },
-    
-    /*
-    '/': { swr: 1800 },
-    */
-    '/about/': { swr: 1800 },
-    '/introduction/**': { swr: true },
-    '/inheritor/submit': { swr: false },
-  }
-})

Файловите разлики са ограничени, защото са твърде много
+ 0 - 14417
package-lock.json


+ 0 - 61
package.json

@@ -1,61 +0,0 @@
-{
-  "name": "wenbao-website",
-  "version": "0.0.0",
-  "private": true,
-  "type": "module",
-  "scripts": {
-    "nuxt-build": "nuxt build",
-    "nuxt-dev": "nuxt dev",
-    "nuxt-generate": "nuxt generate",
-    "nuxt-preview": "nuxt preview",
-    "nuxt-postinstall": "nuxt prepare",
-    "dev": "nuxt dev",
-    "build": "nuxt build",
-    "updater": "node src/scripts/UpdateScript/index.mjs"
-  },
-  "dependencies": {
-    "@ant-design-vue/nuxt": "^1.4.6",
-    "@imengyu/imengyu-utils": "^0.0.19",
-    "@imengyu/js-request-transform": "^0.3.5",
-    "@imengyu/vue-dynamic-form": "^0.1.1",
-    "@imengyu/vue-scroll-rect": "^0.1.3",
-    "@nuxt/icon": "^2.1.1",
-    "@pinia/nuxt": "^0.11.1",
-    "@popperjs/core": "^2.11.8",
-    "@vuemap/vue-amap": "^2.1.12",
-    "ant-design-vue": "^4.2.6",
-    "axios": "^1.9.0",
-    "bootstrap": "^5.3.8",
-    "lodash-es": "^4.17.21",
-    "md5": "^2.3.0",
-    "mitt": "^3.0.1",
-    "mysql2": "^3.16.0",
-    "nuxt": "^4.2.2",
-    "pinia": "^3.0.1",
-    "tslib": "^2.8.1",
-    "vue": "^3.5.13",
-    "vue-router": "^4.5.0",
-    "vue3-carousel": "^0.15.0"
-  },
-  "devDependencies": {
-    "@iconify-json/material-symbols": "^1.2.50",
-    "@inquirer/prompts": "^7.5.3",
-    "@tsconfig/node22": "^22.0.1",
-    "@types/node": "^22.14.0",
-    "@vitejs/plugin-vue": "^5.2.3",
-    "@vitejs/plugin-vue-jsx": "^4.1.2",
-    "@vue/tsconfig": "^0.7.0",
-    "ali-oss": "^6.23.0",
-    "archiver": "^7.0.1",
-    "cli-progress": "^3.12.0",
-    "cli-table3": "^0.6.5",
-    "commander": "^14.0.0",
-    "npm-run-all2": "^7.0.2",
-    "sass": "^1.87.0",
-    "terser": "^5.44.1",
-    "typescript": "~5.8.0",
-    "vite": "^6.2.4",
-    "vite-plugin-vue-devtools": "^7.7.2",
-    "vue-tsc": "^2.2.8"
-  }
-}

BIN
public/favicon.ico


+ 0 - 2
public/robots.txt

@@ -1,2 +0,0 @@
-User-Agent: *
-Disallow:

+ 0 - 23
server/api/channel/nav.ts

@@ -1,23 +0,0 @@
-import { defineEventHandler, EventHandlerRequest } from 'h3';
-import { DB } from '~~/server/db/DB';
-import { createErrorResponse, createSuccessResponse, IResponse } from '~~/server/utils/response';
-
-export default defineEventHandler<EventHandlerRequest, Promise<IResponse<{
-  id: number;
-  model_id: number;
-  name: string;
-  type: 'list'|'link',
-  url: string;
-  outlink: string;
-  diyname: string;
-}[]>>>(async (event) => {
-  try {
-    return createSuccessResponse(await DB.table('pr_cms_channel')
-            .where('status', 'normal')
-            .where('isnav', 1)
-            .select('*')
-            .get());
-  } catch (error) {
-    return createErrorResponse(error);
-  }
-});

+ 0 - 22
server/api/health.ts

@@ -1,22 +0,0 @@
-import { defineEventHandler } from 'h3';
-import { testConnection } from '../config/db';
-
-export default defineEventHandler(async (event) => {
-  try {
-    // 测试数据库连接
-    await testConnection();
-    
-    return {
-      status: 'ok',
-      timestamp: new Date().toISOString(),
-      message: '✅ 服务运行正常,数据库连接成功'
-    };
-  } catch (error) {
-    return {
-      status: 'error',
-      timestamp: new Date().toISOString(),
-      message: '❌ 服务运行异常',
-      error: error instanceof Error ? error.message : String(error)
-    };
-  }
-});

+ 0 - 48
server/config/db.ts

@@ -1,48 +0,0 @@
-import mysql from 'mysql2/promise';
-import { readFileSync } from 'fs';
-import { join } from 'path';
-
-// 尝试从config.json文件读取配置
-let jsonConfig: any = {};
-
-try {
-  // 从项目根目录读取config.json文件
-  const configPath = join(process.cwd(), 'config.json');
-  const configContent = readFileSync(configPath, 'utf-8');
-  jsonConfig = JSON.parse(configContent);
-} catch (error) {
-  console.log('⚠️ 无法读取config.json,将使用环境变量或默认值');
-}
-
-// 数据库配置
-const config = {
-  host: process.env.DB_HOST || jsonConfig.db?.host || 'localhost',
-  port: Number(process.env.DB_PORT) || jsonConfig.db?.port || 3306,
-  user: process.env.DB_USER || jsonConfig.db?.user || 'root',
-  password: process.env.DB_PASSWORD || jsonConfig.db?.password || '',
-  database: process.env.DB_NAME || jsonConfig.db?.database || 'protection_center',
-  waitForConnections: true,
-  connectionLimit: 10,
-  queueLimit: 0
-};
-
-// 创建连接池
-const pool = mysql.createPool(config);
-
-// 测试数据库连接
-async function testConnection() {
-  try {
-    const connection = await pool.getConnection();
-    console.log('✅ 数据库连接成功');
-    connection.release();
-  } catch (error) {
-    console.error('❌ 数据库连接失败:', error);
-    throw error;
-  }
-}
-
-export {
-  pool,
-  testConnection,
-  config
-};

+ 0 - 130
server/db/CommonModel.ts

@@ -1,130 +0,0 @@
-import { type ConvertItemOptions, type ConvertTable, DataConverter } from "@imengyu/js-request-transform";
-
-interface CommonModelConfig {
-  idKey?: string;
-  convertTable?: ConvertTable;
-}
-
-export interface NewCommonModel<T extends CommonModel> {
-  new(): T;
-}
-export type ValidCommonModel<T> = T extends CommonModel ? NewCommonModel<T> : undefined;
-
-export class CommonModel {
-  public _tableName: string;
-  public _config: CommonModelConfig;
-
-  public constructor(tableName: string, config: CommonModelConfig) { 
-    this._tableName = tableName;
-    this._config = config;
-    this._config.convertTable = {
-      ...this._config.convertTable,
-      createdAt: { clientSide: 'date', serverSide: 'string' },
-      updatedAt: { clientSide: 'date', serverSide: 'string' },
-      deletedAt: { clientSide: 'date', serverSide: 'string' },
-    }
-  }
-
-  createdAt = new Date();
-  updatedAt : Date | null = null;
-  deletedAt : Date | null = null;
-
-  public setValue(key: keyof this, value: any) {
-    (this as any)[key] = value;
-    return this;
-  }
-  public setValues(at: {
-    [key in keyof this]?: any;
-  }) {
-    for (const key in at)
-      this.setValue(key, at[key]);
-    return this;
-  }
-
-  public fromServerSide(data: any) {
-    const self = this as Record<string, any>;
-    const options : ConvertItemOptions = {
-      direction: 'client',
-      defaultDateFormat: 'YYYY-MM-DD HH:mm:ss',
-      policy: 'default',
-    }
-    for (const key in data) {
-      if (key.startsWith('_'))
-        continue;
-      const convert = this._config.convertTable?.[key];
-      if (convert) {
-        let value = data[key];
-        if (convert instanceof Array)
-          for (const convertItem of convert) 
-            value = DataConverter.convertDataItem(value, key, convertItem, options, `${key}`, this._tableName);
-        else
-          value = DataConverter.convertDataItem(value, key, convert, options, `${key}`, this._tableName)
-        self[key] = value;
-      } else {
-        self[key] = data[key];
-      }
-    }
-    return this;
-  }
-  public toServerSide() {
-    const self = this as Record<string, any>;
-    const options : ConvertItemOptions = {
-      direction: 'server',
-      defaultDateFormat: 'YYYY-MM-DD HH:mm:ss',
-      policy: 'default',
-    }
-    const result : Record<string, any> = {};
-    for (const key in self) {
-      if (key.startsWith('_'))
-        continue;
-      const convert = this._config.convertTable?.[key];
-      if (convert) {
-        let value = self[key];
-        if (convert instanceof Array)
-          for (const convertItem of convert)
-            value = DataConverter.convertDataItem(value, key, convertItem, options, `${key}`, this._tableName);
-        else
-          value = DataConverter.convertDataItem(value, key, convert, options, `${key}`, this._tableName);
-        result[key] = value;
-      } else {
-        result[key] = self[key];
-      }
-    }
-    return result;
-  }
-}
-
-/**
- * 通用分页返回
- */
-export class CommonPageResult<T> implements ICommonPageResult<T> {
-  public items : T[];
-  public pageIndex : number;
-  public pageSize : number;
-  public allCount : number;
-  public allPage : number;
-  public empty : boolean;
-
-  public constructor(model: ValidCommonModel<T>|undefined, data : any[], pageIndex : number, pageSize : number, allCount : number) {
-    this.items = data.map((item) => {
-      return model ? new model().fromServerSide(item) as T : item as T;
-    });
-    this.pageIndex = pageIndex;
-    this.pageSize = pageSize;
-    this.allCount = allCount;
-    this.allPage = Math.floor(allCount / pageSize) || 0;
-    this.empty = data.length == 0;
-  }
-}
-
-/**
- * 通用分页返回结构
- */
-export interface ICommonPageResult<T> {
-  items : T[];
-  pageIndex : number;
-  pageSize : number;
-  allCount : number;
-  allPage : number;
-  empty : boolean;
-}

+ 0 - 96
server/db/DB.ts

@@ -1,96 +0,0 @@
-import { CommonModel, NewCommonModel } from './CommonModel';
-import { solveSqlPlaceholders, splicingSQL, SqlStatcParams } from './DBUtils'
-import { query, queryAndReturnInsertId } from './Query';
-import { QueryGenerator } from './QueryGenerator';
-
-/**
- * 数据库操作工具类 (SQLITE)
- * 如何使用:
- * DB.table('表名') 构造一个查询器
- * DB.select/update/insert/delete 进行SQL查询,其中:
- *      SQL中参数使用: {?} {参数索引} 来代表参数占位符,参数在args中传入
- */
-export class DB {
-
-  /**
-   * 构造查询器
-   * @param name 当前表名
-   * @returns 返回构造查询器
-   */
-  static table<T extends CommonModel = any>(name : string|NewCommonModel<T>) {
-    return new QueryGenerator<T>(name);
-  }
-
-  /**
-   * 进行 select 查询
-   * @param sql SQL语句
-   * @param args 参数数组
-   * @param staticParams 静态参数
-   * @returns promise 成功则返回查询到的数组,错误则返回错误信息
-   */
-  static async select<T>(sql : string, args : unknown[], staticParams : SqlStatcParams = {}) {
-    let placeholders = solveSqlPlaceholders(sql);
-    let targetSql = splicingSQL(sql, placeholders, args, null, staticParams);
-    return await query(targetSql);
-  }
-  /**
-   * 执行 update 查询
-   * @param sql SQL语句
-   * @param args 参数数组
-   * @returns promise 成功则返回受影响的行数,错误则返回错误信息
-   */
-  static async update(sql : string, args : unknown[]) {
-    let placeholders = solveSqlPlaceholders(sql);
-    let targetSql = splicingSQL(sql, placeholders, args, null, {});
-    return await query(targetSql);
-  }
-  /**
-   * 执行 insert 查询
-   * @param sql SQL语句
-   * @param args 参数数组
-   * @returns promise 成功则返回新插入的ID,错误则返回错误信息
-   */
-  static async insert(sql : string, args : unknown[]) {
-    let placeholders = solveSqlPlaceholders(sql);
-    let targetSql = splicingSQL(sql, placeholders, args, null, {});
-    return await queryAndReturnInsertId(targetSql);
-  }
-  /**
-   * 执行 delete 查询
-   * @param sql SQL语句
-   * @param args 参数数组
-   * @returns promise 成功则返回受影响的行数,错误则返回错误信息
-   */
-  static async delete(sql : string, args : unknown[]) {
-    let placeholders = solveSqlPlaceholders(sql);
-    let targetSql = splicingSQL(sql, placeholders, args, null, {});
-    return await query(targetSql);
-  }
-
-  /**
-   * 手动开始事务
-   */
-  static beginTransaction() {
-    return this.actionSql('BEGIN');
-  }
-  /**
-   * 回滚事务
-   */
-  static rollBack() {
-    return this.actionSql('ROLLBACK');
-  }
-  /**
-   * 提交事务
-   */
-  static commit() {
-    return this.actionSql('COMMIT');
-  }
-  /**
-   * 异步执行sql
-   * @param sql 
-   * @returns 
-   */
-  static async actionSql(sql : string) {
-    return await query(sql);
-  }
-}

+ 0 - 109
server/db/DBUtils.ts

@@ -1,109 +0,0 @@
-import { StringUtils } from "@imengyu/imengyu-utils";
-import { DataModel } from "@imengyu/js-request-transform";
-
-export interface SqlPlaceholder {
-  index: number,
-  length: number,
-  name: string
-}
-
-/**
- * SQL静态参数的结构
- */
-export interface SqlStatcParams {
-  [index: string]: any;
-}
-
-const sqlPlaceholderCache = new Map<string, SqlPlaceholder[]>();
-
-/**
- * 分割sql中的参数占位符
- * @param sql 原始sql
- * @returns 
- */
-export function solveSqlPlaceholders(sql : string) {
-  const hash = StringUtils.stringHashCode(sql);
-
-  let result : SqlPlaceholder[]|null = null;
-
-  if (sqlPlaceholderCache.has(hash)) 
-    result = sqlPlaceholderCache.get(hash)!;
-  if (result)
-    return result;
-
-  result = [];
-
-  let currentBracketsStart = -1;
-  let currentBracketsEnd = -1;
-  let chr = '';
-
-  for(let i = 0; i < sql.length; i++) {
-    chr = sql.charAt(i);
-    if(currentBracketsStart < 0) {
-      if(chr === '{') currentBracketsStart = i;
-    } else {
-      if(chr === '}') {
-        currentBracketsEnd = i;
-        if(currentBracketsEnd - currentBracketsStart > 0) {
-          result.push({
-            index: currentBracketsStart,
-            length: currentBracketsEnd - currentBracketsStart + 1,
-            name: sql.substring(currentBracketsStart + 1, currentBracketsEnd)
-          });
-        } else { 
-          console.warn('Bad sql placeholder, the brackets dose not contains a name , at ' + currentBracketsStart);
-        }
-        currentBracketsEnd = -1;
-        currentBracketsStart = -1;
-      }
-    }
-
-    if(i === sql.length - 1 && currentBracketsStart >= 0) {
-      console.warn('Bad sql placeholder, not found end brackets, at ' + currentBracketsStart);
-    }
-  }
-
-  sqlPlaceholderCache.set(hash, result);
-  return result;
-}
-/**
- * 按占位符和参数拼接最终sql
- * @param sql 原始sql
- * @param placeholders 占位符数据
- * @param args 传入参数
- * @returns 
- */
-export function splicingSQL(sql : string, placeholders : SqlPlaceholder[], args: unknown[], fn : Function|null, staticParams : SqlStatcParams) {
-  let result = '';
-  let startOffset = 0;
-  let funcArgNames = (fn ? (fn.toString()
-    .replace(/((\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s))/mg,'')
-    .match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)?.[1]
-    .split(/,/)) : null) || null;
-
-  for (let i = 0; i < args.length; i++) {
-    const item = args[i];
-    if (item instanceof DataModel) 
-      args[i] = item.toServerSide();
-  }
-
-  for(let i = 0; i < placeholders.length; i++) {
-    let p = placeholders[i];
-    let index = p.name === '?' ? i : parseInt(p.name);
-    let argval = null;
-    if(index >= 0) argval = args[index];
-    else {
-      let ii = funcArgNames ? funcArgNames.indexOf(p.name) : -1;
-      argval = ii >= 0 ? args[ii] : staticParams[p.name];
-    }
-
-    result = result.concat(sql.substring(startOffset, p.index), (argval !== null ? argval : ''));
-    startOffset = p.index + p.length;
-
-    if(i === placeholders.length - 1) {
-      result = result.concat(sql.substring(startOffset));
-    }
-  }
-
-  return result;
-}

+ 0 - 73
server/db/Query.ts

@@ -1,73 +0,0 @@
-import { QueryResult } from 'mysql2';
-import { pool } from '../config/db';
-
-/**
- * 执行SELECT查询
- * @param sql SQL查询语句
- * @param params 查询参数
- * @returns 查询结果
- */
-export async function query<T extends QueryResult = any>(sql: string, params: any[] = []): Promise<T[]> {
-  const [rows] = await pool.execute<T>(sql, params);
-  return rows as T[];
-}
-
-export async function queryAndReturnInsertId(sql: string, params: any[] = []) : Promise<number> {
-  const [rows] = await pool.execute(sql, params);
-  const [insertIdRows] = await query('SELECT LAST_INSERT_ID() AS id');
-  return insertIdRows.id;
-}
-
-/**
- * 执行INSERT查询
- * @param sql SQL插入语句
- * @param params 查询参数
- * @returns 插入结果,包含insertId
- */
-export async function insert(sql: string, params: any[] = []): Promise<{ insertId: number }> {
-  const [result] = await pool.execute(sql, params);
-  return result as { insertId: number };
-}
-
-/**
- * 执行UPDATE查询
- * @param sql SQL更新语句
- * @param params 查询参数
- * @returns 更新结果,包含affectedRows
- */
-export async function update(sql: string, params: any[] = []): Promise<{ affectedRows: number }> {
-  const [result] = await pool.execute(sql, params);
-  return result as { affectedRows: number };
-}
-
-/**
- * 执行DELETE查询
- * @param sql SQL删除语句
- * @param params 查询参数
- * @returns 删除结果,包含affectedRows
- */
-export async function remove(sql: string, params: any[] = []): Promise<{ affectedRows: number }> {
-  const [result] = await pool.execute(sql, params);
-  return result as { affectedRows: number };
-}
-
-/**
- * 执行事务
- * @param callback 事务回调函数
- * @returns 事务执行结果
- */
-export async function transaction<T>(callback: (connection: any) => Promise<T>): Promise<T> {
-  const connection = await pool.getConnection();
-  
-  try {
-    await connection.beginTransaction();
-    const result = await callback(connection);
-    await connection.commit();
-    return result;
-  } catch (error) {
-    await connection.rollback();
-    throw error;
-  } finally {
-    connection.release();
-  }
-}

Файловите разлики са ограничени, защото са твърде много
+ 0 - 1071
server/db/QueryGenerator.ts


+ 0 - 3
server/tsconfig.json

@@ -1,3 +0,0 @@
-{
-  "extends": "../.nuxt/tsconfig.server.json"
-}

+ 0 - 27
server/utils/response.ts

@@ -1,27 +0,0 @@
-export interface IResponse<T> {
-  status: boolean;
-  message: string;
-  data?: T;
-}
-
-export function createSuccessResponse<T>(data?: T, message?: string): IResponse<T> {
-  return {
-    status: true,
-    message: message || '成功',
-    data
-  };
-}
-export function createErrorResponse( error?: any, message?: string): IResponse<any> {
-  return {
-    status: false,
-    message: message || '错误',
-    data: error
-  };
-}
-export function createResponse<T>(status: boolean, message: string, data?: T): IResponse<T> {
-  return {
-    status,
-    message,
-    data
-  };
-}

+ 0 - 60
src/App.vue

@@ -1,60 +0,0 @@
-<template>
-  <a-config-provider
-    :locale="zhCN"
-    :theme="{
-      token: {
-        colorPrimary: '#bd4b36',
-      },
-    }"
-  >
-    <NuxtLoadingIndicator />
-    <NavBar />
-    <main>
-      <NuxtPage />
-    </main>
-    <Footer />
-  </a-config-provider>
-</template>
-
-<script setup lang="ts">
-import { onMounted, watch } from 'vue';
-import { useRoute } from 'vue-router'
-import { useAuthStore } from './stores/auth';
-import { initAMapApiLoader } from '@vuemap/vue-amap';
-import { registryConvert } from './common/ConvertRgeistry'
-import zhCN from 'ant-design-vue/es/locale/zh_CN';
-import NavBar from './components/NavBar.vue';
-import Footer from './components/Footer.vue';
-
-if (import.meta.client) {
-  initAMapApiLoader({
-    key: '212b86dc49a5116a34e945d31da7ad14',
-    securityJsCode: '46cae8205a707cfaf5801abcc4301566',
-  });
-} 
-registryConvert();
-
-const authStore = useAuthStore();
-
-onMounted(() => {
-  if (import.meta.server)
-    return;
-  authStore.loadLoginState();
-});
-
-const route = useRoute();
-
-watch(route, () => {
-  window.scrollTo({
-    top: 0,
-    behavior: 'instant'
-  })
-});
-</script>
-
-<style>
-@import "./assets/scss/main.scss";
-@import "vue3-carousel/carousel.css";
-@import "@vuemap/vue-amap/dist/style.css";
-@import "@imengyu/vue-scroll-rect/lib/vue-scroll-rect.css";
-</style>

+ 0 - 12
src/api/NotConfigue.ts

@@ -1,12 +0,0 @@
-import { DataModel } from '@imengyu/js-request-transform';
-import { AppServerRequestModule } from './RequestModules';
-
-export class CommonApi extends AppServerRequestModule<DataModel> {
-
-  constructor() {
-    super();
-    this.config.modelClassCreator = DataModel;
-  }
-}
-
-export default new CommonApi();

+ 0 - 207
src/api/RequestModules.ts

@@ -1,207 +0,0 @@
-
-/**
- * 这里写的是业务相关的:
- * * 请求数据处理函数。
- * * 自定义请求模块。
- * * 自定义错误报告处理函数。
- */
-
-import AppCofig from "@/common/config/AppCofig";
-import ApiCofig from "@/common/config/ApiCofig";
-import { 
-  RequestApiConfig,
-  RequestApiError, RequestApiResult, type RequestApiErrorType, 
-  RequestCoreInstance, RequestOptions, 
-  defaultResponseDataGetErrorInfo, defaultResponseDataHandlerCatch, 
-  RequestResponse,
-  WebFetchImplementer,
-  StringUtils,
-  appendGetUrlParams, 
-  appendPostParams,
-} from "@imengyu/imengyu-utils";
-import type { DataModel, KeyValue, NewDataModel } from "@imengyu/js-request-transform";
-import { useAuthStore } from "@/stores/auth";
-import { Modal } from "ant-design-vue";
-
-/**
- * 不报告错误的 code
- */
-const notReportErrorCode = [401] as number[];
-const notReportMessages = [
-  /请授权绑定手机号/g,
-] as RegExp[];
-function matchNotReportMessage(str: string) {
-  for (let i = 0; i < notReportMessages.length; i++) {
-    if (notReportMessages[i]?.test(str))
-      return true;
-  }
-  return false;
-}
-
-//请求拦截器
-function requestInceptor(url: string, req: RequestOptions) {
-  //获取store中的token,追加到头;
-  const autoStore = useAuthStore();
-  if (StringUtils.isNullOrEmpty((req.header as KeyValue).token as string)) {
-    req.header['token'] = autoStore.token;
-    req.header['__token__'] = autoStore.token;
-  }
-  if (req.method == 'GET') {
-    //追加GET参数
-    url = appendGetUrlParams(url, 'main_body_id', ApiCofig.mainBodyId);
-  } else {
-    req.data = appendPostParams(req.data,'main_body_id', ApiCofig.mainBodyId);
-  }
-  return { newUrl: url, newReq: req };
-}
-//响应数据处理函数
-function responseDataHandler<T extends DataModel>(response: RequestResponse, req: RequestOptions, resultModelClass: NewDataModel|undefined, instance: RequestCoreInstance<T>, apiName: string | undefined): Promise<RequestApiResult<T>> {
-  return new Promise<RequestApiResult<T>>((resolve, reject) => {
-    const method = req.method || 'GET';
-    response.json().then((json) => {
-      if (response.ok) {
-        if (!json) {
-          reject(new RequestApiError(
-            'businessError',
-            '后端未返回数据',
-            '',
-            response.status,
-            null,
-            null,
-            req,
-            apiName,
-            response.url
-          ));
-          return;
-        }
-
-        //code == 0 错误
-        if (json.code === 0) {
-          handleError();
-          return;
-        }
-
-        //处理后端的数据
-        let message = '未知错误';
-        let data = {} as any;
-
-        //后端返回格式不统一,所以在这里处理格式
-        if (typeof json.data === 'object') {
-          data = json.data;
-          message = json.data?.msg || response.statusText;
-        }
-        else {
-          //否则返回上层对象
-          data = json;
-          message = json.msg || response.statusText;
-        }
-
-        resolve(new RequestApiResult(
-          resultModelClass ?? instance.config.modelClassCreator,
-          json?.code || response.status,
-          message,
-          data,
-          json
-        ));
-      }
-      else {
-        handleError();
-      }
-
-      function handleError() {
-        let errType : RequestApiErrorType = 'unknow';
-        let errString = '';
-        let errCodeStr = '';
-
-        if (typeof json.message === 'string') 
-          errString = json.message;
-        if (typeof json.msg === 'string') 
-          errString += json.msg;
-
-        if (StringUtils.isStringAllEnglish(errString))
-          errString = '服务器返回:' + errString;
-
-        //错误处理
-        if (errString) {
-          //如果后端有返回错误信息,则收集错误信息并返回
-          errType = 'businessError';
-          if (typeof json.data === 'object' && json.data?.errmsg) {
-            errString += '\n' + json.data.errmsg;
-          }
-          if (typeof json.errors === 'object') {
-            for (const key in json.errors) {
-              if (Object.prototype.hasOwnProperty.call(json.errors, key)) {
-                errString += '\n' + json.errors[key];
-              }
-            }
-          }
-        } else {
-          const res = defaultResponseDataGetErrorInfo(response, json);
-          errType = res.errType;
-          errString = res.errString;
-          errCodeStr = res.errCodeStr;
-        }
-
-        reject(new RequestApiError(
-          errType,
-          errString,
-          errCodeStr,
-          response.status,
-          null,
-          null,
-          req,
-          apiName,
-          response.url
-        ));
-      }
-    }).catch((err) => {
-      //错误统一处理
-      defaultResponseDataHandlerCatch(method, req, response, null, err, apiName, response.url, reject, instance);
-    });
-  });
-}
-//错误报告处理
-function responseErrReoprtInceptor<T extends DataModel>(instance: RequestCoreInstance<T>, response: RequestApiError) {
-  return (
-    (response.errorType !== 'businessError' && response.errorType !== 'networkError') ||
-    notReportErrorCode.indexOf(response.code) >= 0 ||
-    matchNotReportMessage(response.errorMessage) === true
-  );
-}
-
-//错误报告处理
-export function reportError<T extends DataModel>(instance: RequestCoreInstance<T>, response: RequestApiError | Error) {
-  if (import.meta.env.DEV) {
-    if (response instanceof RequestApiError) {
-      
-    } else {
-    }
-  } else {    
-    let errMsg = '';
-    if (response instanceof RequestApiError)
-      errMsg = response.errorMessage + '。';
-      
-    errMsg += '服务出现了异常,请稍后重试或联系客服。';
-    errMsg += '版本:' + AppCofig.version;
-
-    Modal.error({
-      title: '抱歉',
-      content: errMsg,
-    });
-}
-}
-
-/**
- * App服务请求模块
- */
-export class AppServerRequestModule<T extends DataModel> extends RequestCoreInstance<T> {
-  constructor() {
-    super(WebFetchImplementer);
-    this.config.baseUrl = ApiCofig.serverProd;
-    this.config.errCodes = []; //
-    this.config.requestInceptor = requestInceptor;
-    this.config.responseDataHandler = responseDataHandler;
-    this.config.responseErrReoprtInceptor = responseErrReoprtInceptor;
-    this.config.reportError = reportError;
-  }
-}

+ 0 - 15
src/api/Utils.ts

@@ -1,15 +0,0 @@
-export function transformSomeToArray(source: any) {
-  if (typeof source === 'string') 
-    return source.split(','); 
-  if (typeof source === 'object') {
-    if (source instanceof Array)
-      return source; 
-    else {
-      const arr = [];
-      for (const key in source)
-        arr.push(source[key]);
-      return arr;
-    }
-  }
-  return source;
-}

+ 0 - 68
src/api/auth/UserApi.ts

@@ -1,68 +0,0 @@
-import { DataModel } from '@imengyu/js-request-transform';
-import { AppServerRequestModule } from '../RequestModules';
-
-
-export class LoginResult extends DataModel<LoginResult> {
-  constructor() {
-    super(LoginResult, "登录结果");
-    this._convertTable = {
-      userInfo: { clientSide: 'object', clientSideChildDataModel: UserInfo },
-    };
-    this._nameMapperServer = {
-      'userinfo': 'userInfo',
-      'mainBodyUserInfo': 'userInfo',
-    }
-    this._afterSolveServer = () => {
-      if (this.mainBodyUserInfo) {
-        this.userInfo.token = this.mainBodyUserInfo.token;
-      }
-    };
-  }
-  userInfo !:UserInfo;
-  mainBodyUserInfo?:UserInfo;
-}
-export class UserInfo extends DataModel<UserInfo> {
-  constructor() {
-    super(UserInfo, "用户信息");
-    this.setNameMapperCase('Camel', 'Snake');
-    this._convertTable = {
-      id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
-    }
-  }
-
-  expiresIn = 0;
-  id = 0;
-  userId = 0;
-  mobile = '';
-  nickname = '';
-  avatar = '';
-  username = '';
-  token = '';
-}
-
-export class UserApi extends AppServerRequestModule<DataModel> {
-
-  constructor() {
-    super();
-  }
-  async loginAdmin(data?: {
-    account: string,
-    password: string,
-  }) {
-    const form = new FormData();
-    form.append('account', data?.account || '');
-    form.append('password', data?.password || '');
-    return (await this.post('/user/adminLogin', form, '登录', undefined, LoginResult)).data as LoginResult;
-  }
-  async getUserInfo(main_body_user_id: number) {
-    return (await this.post('/content/main_body_user/getMainBodyUser', {
-      main_body_user_id,
-    }, '获取用户信息', undefined, UserInfo)).data as UserInfo;
-  }
-  async refresh() {
-    return (await this.post('/content/main_body_user/refreshUser', {
-    }, '刷新用户', undefined, LoginResult)).data as LoginResult;
-  }
-}
-
-export default new UserApi();

BIN
src/assets/fonts/PingFang Regular.ttf


BIN
src/assets/fonts/STSongti-SC-Black.ttf


BIN
src/assets/fonts/STSongti-SC-Black.woff


BIN
src/assets/fonts/STSongti-SC-Black.woff2


BIN
src/assets/images/about-logo.png


BIN
src/assets/images/badge.png


BIN
src/assets/images/box-service.png


BIN
src/assets/images/button-active.png


BIN
src/assets/images/button-deactive.png


BIN
src/assets/images/footer-bg.png


BIN
src/assets/images/icon-contract.png


BIN
src/assets/images/icon-explore.png


BIN
src/assets/images/icon-join.png


BIN
src/assets/images/tab-active.png


BIN
src/assets/images/test-header-1.png


BIN
src/assets/images/test-header-2.png


BIN
src/assets/images/test-header-3.png


BIN
src/assets/images/test-header-4.png


BIN
src/assets/images/test-header-5.png


BIN
src/assets/images/test-header-6.png


BIN
src/assets/images/test-header-7.png


BIN
src/assets/images/test-header-8.png


BIN
src/assets/images/xuexi.png


+ 0 - 8
src/assets/scss/fix.scss

@@ -1,8 +0,0 @@
-.carousel-light {
-  --vc-nav-color: #fff;
-  --vc-clr-primary: #fff;
-  --vc-clr-secondary: #cfcfcfc4;
-  --vc-pgn-background-color: #cfcfcfc4;
-  --vc-clr-white: #333333;
-  --vc-pgn-active-color: var(--vc-clr-primary)
-}

+ 0 - 36
src/assets/scss/fonts.scss

@@ -1,36 +0,0 @@
-@font-face {
-  font-family: nzgrRuyinZouZhangKai;
-  src: 
-    url('@/assets/fonts/nzgrRuyinZouZhangKai.woff') format('woff')
-    url('@/assets/fonts/nzgrRuyinZouZhangKai.ttf') format('truetype')
-    url('@/assets/fonts/nzgrRuyinZouZhangKai.woff2') format('woff2');
-  font-weight: normal;
-}
-@font-face {
-  font-family: STSongtiSCBlack;
-  src: 
-    url('@/assets/fonts/STSongti-SC-Black.woff') format('woff') 
-    url('@/assets/fonts/STSongti-SC-Black.ttf') format('truetype')
-    url('@/assets/fonts/STSongti-SC-Black.woff2') format('woff2');
-  font-weight: normal;
-}
-@font-face {
-  font-family: Impact;
-  src: 
-    url('@/assets/fonts/Impact.woff') format('woff') 
-    url('@/assets/fonts/Impact.ttf') format('truetype')
-    url('@/assets/fonts/Impact.woff2') format('woff2');
-  font-weight: normal;
-}
-@font-face {
-  font-family: SourceHanSerifCNBold;
-  src: 
-    url('@/assets/fonts/SourceHanSerifCN-Bold.woff') format('woff') 
-    //url('@/assets/fonts/SourceHanSerifCN-Bold.ttf') format('truetype')
-    ;//url('@/assets/fonts/SourceHanSerifCN-Bold.woff2') format('woff2');
-  font-weight: normal;
-}
-
-.font-SourceHanSerifCNBold {
-  font-family: SourceHanSerifCNBold;
-}

+ 0 - 836
src/assets/scss/main.scss

@@ -1,836 +0,0 @@
-@use "./fonts.scss";
-@use "./fix.scss";
-@use "sass:list";
-@use "sass:math";
-
-/* 基础样式 */
-:root {
-  --color-primary: #db5f46;
-  --color-secondary: #ac361e;
-  --color-text: #333;
-  --color-text-light: #fff;
-  --color-text-dark: #000;
-  --color-text-secondary: #888;
-  --color-border: #ddd;
-  --color-box: #f9fafb;
-  --color-box-inset: #e5e7eb;
-  --color-box-hover: #c4a29b;
-  --color-mask: rgba(0, 0, 0, 0.4);
-
-  --swiper-navigation-color: var(--color-primary);
-  --swiper-pagination-color: var(--color-primary);
-  --container-width: 1200px;
-  --selection-max-width: 1250px;
-}
-
-@font-face {
-  font-family: 'PingFang SC';
-  src: url('@/assets/fonts/PingFang Regular.ttf') format('truetype');
-}
-
-@font-face {
-  font-synthesis: none;
-  font-family: "STSongtiSC";
-  src: url('@/assets/fonts/STSongti-SC-Black.woff2') format('woff2');
-}
-
-html, body {
-  position: relative;
-  color: var(--color-text);
-  line-height: 1.6;
-  font-family: 'PingFang SC', 'STSongtiSC', "Microsoft YaHei", sans-serif !important;
-  margin: 0;
-  padding: 0;
-}
-
-a {
-  transition: color 0.3s;
-  text-decoration: none;
-  color: var(--color-text);
-
-  &:hover {
-    color: var(--color-primary);
-  }
-}
-ul {
-  list-style: none;
-}
-
-/* 头部样式 */
-header {
-  padding: 15px 0;
-  background-color: #fff;
-  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
-
-  .inner {
-    display: flex;
-    flex-direction: row;
-    justify-content: space-between;
-    align-items: center;
-    max-width: var(--container-width);
-    margin: 0 auto;
-  }
-  .logo {
-    font-family: 'STSongtiSC', "Microsoft YaHei", sans-serif;
-    font-size: 24px;
-    font-weight: bold;
-    color: var(--color-primary);
-  }
-  .mobile-menu-toggle {
-    display: none;
-    width: 60px;
-    height: 60px;
-  }
-}
-
-button {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  border: none;
-  background-color: var(--color-primary);
-  color: var(--color-text-light);
-  
-  &:hover {
-  background-color: var(--color-secondary);
-  }
-  & i {
-    color: var(--color-text-light);
-  }
-
-  &.bordered {
-    color: var(--color-text);
-    background-color: #f3f3f3;
-    background-repeat: no-repeat;
-    background-size: 100% 100%;
-    /* 九宫格切图实现 - 防止图片变形 */
-    border-image: url('@/assets/images/button-deactive.png') 25 25 25 25 stretch;
-    border-width: 10px;
-    border-style: solid;
-    background-clip: padding-box;
-    padding: 0 16px;
-    margin-right: 8px;
-    cursor: pointer;
-    font-size: 14px;
-    font-family: "STSongtiSC";
-    box-sizing: border-box;
-
-    &:hover,
-    &.active {
-      color: var(--color-text-light);
-      background-color: #d74a2e;
-      border-image: url('@/assets/images/button-active.png') 25 25 25 25 stretch;
-    }
-  }
-}
-.bordered-input {
-  color: var(--color-text);
-  border-style: solid;
-  border-width: 10px;
-  border-image: url('@/assets/images/button-deactive.png') 25 25 25 25 stretch;
-
-  input {
-    background-color: #f3f3f3;
-    border: none;
-    font-size: 15px;
-    font-family: "STSongtiSC";
-    color: var(--color-text);
-    width: 220px;
-
-    &:focus {
-      outline: none;
-    }
-  }
-}
-
-/* 导航样式 */
-nav.main-nav {
-  position: relative;
-
-  .nav-list {
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    margin-bottom: 0;
-  }
-  .nav-item {
-    position: relative;
-
-    &.active a {
-      font-weight: bold;
-      color: var(--color-primary);
-    }
-  }
-  a {
-    display: block;
-    color: var(--color-text);
-    padding: 12px 20px;
-    font-weight: 500;
-    transition: background-color 0.3s;
-
-    &:hover, &:active {
-      color: var(--color-primary);
-    }
-  }
-}
-
-
-.search-bar {
-  position: relative;
-
-  button {
-    width: 60px;
-    height: 60px;
-    i {
-      color: var(--color-text-light);
-    }
-  }
-  input {
-    position: absolute;
-    right: 0;
-    padding: 8px 15px;
-    border: 1px solid var(--color-border);
-    outline: none;
-    width: 200px;
-    height: 60px;
-    transition: all 0.3s;
-    opacity: 0;
-    z-index: 20;
-
-    &.show {
-      width: 250px;
-      opacity: 1;
-      border-color: var(--color-primary);
-    }
-  }
-}
-
-/* 主要内容区域 */
-.main-content {
-  position: relative;
-  width: 100%;
-  padding: 40px 0;
-}
-.main-box {
-  background-color: var(--color-box);
-  border-radius: 10px;
-  padding: 26px;
-}
-.main-hr {
-  border-color: var(--color-border);
-  margin: 50px 0;
-}
-.main-header-image {
-  width: 100%;
-  height: 400px;
-  object-fit: cover;
-}
-
-/* 标题样式 */
-.section-title {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  justify-content: space-between;
-  margin-bottom: 20px;
-  padding-bottom: 10px;
-  border-bottom: 2px solid var(--color-primary);
-
-  &.center {
-    justify-content: center;
-  }
-  &.large {
-    border-bottom: none;
-
-    h2 {
-      font-size: 30px;
-
-      &.icon {
-        &::before, &::after {
-          width: 28px;
-          height: 28px;
-          background-size: 28px 28px;
-        }
-      }
-    }
-  }
-
-  h2 {
-    position: relative;
-    display: flex;
-    flex-direction: row;
-    align-items: center;
-    font-family: "STSongtiSC";
-    font-synthesis: none;
-    font-size: 20px;
-    color: var(--color-primary);
-    margin-right: 15px;
-
-    i {
-      margin-right: 10px;
-    }
-
-    &.button {
-      cursor: pointer;
-      color: var(--color-text);
-      
-      &.icon {
-        &::before, &::after {
-          display: none;
-        }
-      }
-
-      &.active {
-        &.icon {
-          &::before, &::after {
-            display: inline-block;
-          }
-        }
-        color: var(--color-primary);
-      }
-    }
-    &.icon {
-      &::before, &::after {
-        display: inline-block;
-        content: '';
-        margin: 0 10px;
-        width: 20px;
-        height: 20px;
-        background-size: 20px 20px;
-        background-image: url('@/assets/images/badge.png');
-        background-position: center; 
-      }
-    } 
-  }
-  .sub-title {
-    color: #666;
-    padding-left: 15px;
-    border-left: 1px solid #ddd;
-  }
-}
-.section-more {
-  margin-left: auto;
-  color: #666;
-
-  &:hover {
-    color: var(--color-primary);
-  }
-}
-
-/* 通知公告区域 */
-.notices-section {
-  display: flex;
-  gap: 30px;
-  margin-bottom: 40px;
-}
-.notice-item {
-  position: relative;
-  padding: 15px 0;
-  border-bottom: 1px dashed #ddd;
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-
-  > div {
-    max-width: 86%;
-    flex: 1;
-    flex-direction: column;
-    margin-right: 15px;
-    overflow: hidden;
-  } 
-  p {
-    opacity: 0;
-    height: 0px;
-    margin-bottom: 0;
-    transition: all ease-in-out 0.25s;
-  }
-
-  &:last-child {
-    border-bottom: none;
-  }
-  &:hover {
-    /* .notice-title {
-      font-size: 20px;
-      opacity: 1;
-    } */
-    /* p {
-      opacity: 1;
-      height: 85px;
-    } */
-  }
-}
-.notice-title {
-  display: block;
-  width: 100%;
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  transition: all 0.3s;
-}
-.notice-date {
-  color: #999;
-  font-size: 14px;
-}
-.featured-image {
-  border-radius: 6px;
-  overflow: hidden;
-  position: relative;
-  img {
-    width: 100%;
-    height: 100%;
-    object-fit: cover;
-    transition: transform 0.5s;
-  }
-  .caption {
-    position: absolute;
-    bottom: 0;
-    left: 0;
-    right: 0;
-    background-color: var(--color-mask);
-    color: var(--color-text-light);
-    padding: 8px 20px;
-    h3 {
-      font-size: 18px;
-      margin-bottom: 5px;
-    }
-    p {
-      font-size: 14px;
-      opacity: 0.9;
-    }
-  }
-
-  &:hover img {
-    transform: scale(1.05);
-  }
-}
-
-.news-item {
-  padding: 12px 0;
-  border-bottom: 1px dashed #ddd;
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-
-  &:last-child {
-    border-bottom: none;
-  }
-  &.dark {
-    .title {
-      color: var(--color-text-dark);
-    }
-  }
-
-  .title {
-    flex: 1;
-    margin-right: 15px;
-    white-space: nowrap;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    transition: color 0.3s;
-
-    &:hover {
-      color: var(--color-primary);
-    }
-  }
-  .date {
-    color: #999;
-    font-size: 14px;
-  }
-}
-
-/* 精彩推荐区域 */
-.featured-section {
-  margin-bottom: 40px;
-}
-.featured-grid {
-  display: grid;
-  grid-template-columns: repeat(3, 1fr);
-  gap: 20px;
-}
-.featured-card {
-  position: relative;
-  border: 1px solid #eee;
-  border-radius: 5px;
-  overflow: hidden;
-  transition:
-    transform 0.3s,
-    box-shadow 0.3s;
-  cursor: pointer;
-
-  p {
-    position: absolute;
-    bottom: 0;
-    left: 0;
-    right: 0;
-    background-color: var(--color-mask);
-    color: var(--color-text-light);
-    padding: 15px;
-    z-index: 1;
-    margin-bottom: 0;
-  }
-  img {
-    width: 100%;
-    height: 200px;
-    object-fit: cover;
-  }
-  &:hover {
-    transform: translateY(-5px);
-    box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
-  }
-}
-
-/* 公共服务区域 */
-.services-section {
-  margin-bottom: 40px;
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-
-  .title {
-    width: 220px;
-    height: 220px;
-    z-index: 10;
-  }
-}
-.services-grid {
-  flex: 1;
-  display: grid;
-  grid-template-columns: repeat(3, 1fr);
-  gap: 30px;
-  margin-top: 60px;
-  background-color: #fff1ee;
-  padding: 0px 30px 40px 230px;
-  border-radius: 15px;
-  margin-left: -180px;
-  z-index: 0;
-  z-index: 0;
-}
-.service-card {
-  background-color: #fff;
-  border-radius: 8px;
-  padding: 30px 20px;
-  margin-top: -15px;
-  box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
-  transition:
-    transform 0.3s,
-    box-shadow 0.3s;
-  h3 {
-    font-size: 15px;
-  }
-  .icon {
-    width: 40px;
-    height: 40px;
-    margin-bottom: 10px;
-  }
-  &:hover {
-    transform: translateY(-5px);
-    box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
-  }
-}
-
-/* 底部区域 */
-footer {
-  color: #fff;
-  background-color: #f5f5f5;
-  background-image: url('@/assets/images/footer-bg.png');
-  background-position: center;
-  background-size: cover;
-  padding: 40px 0 20px;
-
-  a {
-    color: #ddd;
-
-    &:hover, &:active {
-      color: #fff;
-    }
-  }
-}
-.footer-links {
-  display: flex;
-  justify-content: center;
-  margin-bottom: 20px;
-  flex-wrap: wrap;
-}
-.footer-link {
-  margin: 0 15px;
-  color: #eee;
-
-  &:hover {
-    color: #fcacb9;
-  }
-}
-.footer-info {
-  text-align: center;
-  color: #eee;
-  font-size: 14px;
-  line-height: 1.8;
-}
-.footer-select {
-  text-align: center;
-  margin-bottom: 20px;
-  color: var(--color-text-light);
-
-  select {
-    color: #666;
-    padding: 8px 45px 8px 15px;
-    border: 1px solid rgba(221, 221, 221, 0.6);
-    border-radius: 6px;
-    background-color: rgba(255, 255, 255, 0.25);
-    cursor: pointer;
-  }
-}
-
-.sidebar {
-  .title {
-    position: relative;
-    padding: 20px;
-    background-color: var(--color-primary);
-    color: var(--color-text-light);
-    text-align: center;
-    border-radius: 4px 4px 0 0;
-    background-image: url('@/assets/images/tab-active.png');
-    background-size: 100% 100%;
-    background-repeat: no-repeat;
-    height: 100px;
-    margin-bottom: 20px;
-    border: 1px solid var(--color-border);
-    border-radius: 4px;
-
-    h2 {
-      position: relative;
-      margin: 0;
-      font-size: 30px;
-      font-weight: bold;
-      font-family: "STSongtiSC";
-      line-height: 50px;
-      z-index: 2;
-    }
-  }
-}
-.sidebar-menu {
-  list-style: none;
-  padding: 0;
-  margin: 0;
-  
-  li {
-    border-bottom: 1px solid var(--color-border);
-
-
-    &.active a {
-      color: var(--color-primary);
-    }
-    a {
-      display: flex;
-      flex-direction: row;
-      justify-content: space-between;
-      align-items: center;
-      padding: 15px 20px;
-      color: var(--color-text);
-      text-decoration: none;
-      transition: all 0.3s;
-      
-      i {
-        color: var(--color-primary);
-      }
-
-      &:hover {
-        color: var(--color-primary);
-      }
-    }
-    &:last-child {
-      border-bottom: none;
-    }
-  }
-}
-
-.pagination {
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  margin-top: 20px;
-
-  li {
-    margin: 0 5px;
-    //border-radius: 6px;
-
-    a {
-      padding: 6px 10px;
-      color: var(--color-text-secondary)!important;
-    }
-    
-    &.active {
-
-      a {
-        border-color: var(--color-primary)!important;
-        background-color: var(--color-primary)!important;
-        color: var(--color-text-light)!important;
-      }
-    }
-  }
-}
-
-.news-detail {
-  h1 {
-    text-align: center;
-    font-size: 30px;
-  }
-  .times {
-    display: flex;
-    flex-direction: row;
-    justify-content: space-around;
-    align-items: center;
-    color: var(--color-text-secondary);
-    padding: 20px 0;
-    margin-bottom: 20px;
-    border-bottom: 1px solid var(--color-border);
-
-    p {
-      margin-bottom: 0;
-      font-size: 15px;
-    }
-  }
-}
-
-/* 联系我们页面样式 */
-.contact-info {
-  padding-right: 30px;
-}
-.info-list {
-  padding-left: 0;
-}
-.info-item {
-  display: flex;
-  flex-direction: row;
-  padding: 10px 0;
-  align-items: center;
-  transition: transform 0.3s, box-shadow 0.3s;
-
-  &:hover {
-    transform: translateY(-3px);
-    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
-  }
-  i {
-    color: var(--color-primary);
-    font-size: 20px;
-    margin-right: 15px;
-    width: 30px;
-    text-align: center;
-  }
-}
-.map-section {
-  position: relative;
-  height: 100%;
-}
-#map-container {
-  border-radius: 8px;
-  overflow: hidden;
-  margin-bottom: 20px;
-}
-
-.no-news {
-  text-align: center;
-  font-size: 18px;
-  color: var(--color-text-secondary);
-  margin: 30px 0;
-}
-
-@media (max-width: 992px) {
-  .featured-image {
-    width: 100%;
-    height: 300px;
-  }
-  .featured-grid {
-    grid-template-columns: repeat(2, 1fr);
-  }
-  header { 
-    .inner {
-      padding: 0 30px;
-    }
-    .logo {
-      font-size: 20px;
-    }
-  }
-  nav.main-nav .nav-item {
-    padding: 0;
-    a {
-      padding: 10px 5px;
-    }
-  }
-}
-
-.swiper-slide {
-  img {
-    object-fit: cover;
-  }
-}
-
-@media (max-width: 768px) { 
-  header { 
-    .inner {
-      padding: 0 30px;
-    }
-    .logo {
-      font-size: 20px;
-    }
-    .mobile-menu-toggle {
-      display: unset;
-      width: 40px;
-      height: 40px;
-    }
-    .search-bar button {
-      width: 40px;
-      height: 40px;
-    }
-  }
-  nav.main-nav {
-    position: absolute;
-    top: 0;
-    opacity: 0;
-    left: 0;
-    right: 0;
-    padding: 20px;
-    background-color: var(--color-box);
-    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
-    transition: all 0.3s;
-    transform: translateY(-100%);
-    z-index: 100;
-
-    &.show {
-      display: block;
-      opacity: 1;
-      transform: translateY(0%);
-    }
-    .nav-list {
-      flex-direction: column;
-    }
-    .nav-item {
-      margin-bottom: 10px;
-    }
-  }
-  .services-grid {
-    grid-template-columns: 1fr;
-  }
-}
-
-@media (max-width: 576px) {
-  .featured-grid {
-    grid-template-columns: 1fr;
-  }
-  .main-box {
-    padding: 15px;
-  }
-  .services-section {
-    align-items: flex-start;
-  }
-  .services-grid {
-    padding: 210px 30px 40px 30px;
-  }
-  header { 
-    .inner {
-      padding: 0 20px;
-    }
-    .logo {
-      font-size: 18px;
-    }
-  }
-}

+ 0 - 63
src/assets/scss/scroll.scss

@@ -1,63 +0,0 @@
-/* Common Scroll bar */
-
-/* PC Scrollbar */
-
-@mixin pc-hide-scrollbar(){
-  &::-webkit-scrollbar {
-      width: 5px;
-      height: 5px;
-  }
-  &::-webkit-scrollbar-thumb {
-      background: transparent;
-
-      &:hover {
-        background: transparent;
-      }
-  }
-  &::-webkit-scrollbar-track {
-      background: transparent;
-  }
-}
-
-@mixin pc-fix-scrollbar(){
-    &::-webkit-scrollbar {
-        width: 5px;
-        height: 5px;
-    }
-    &::-webkit-scrollbar-thumb {
-        background: #707070;
-        border-radius: 3px;
-
-        &:hover {
-            background: #e0e0e0;
-        }
-    }
-    &::-webkit-scrollbar-track {
-        background: transparent;
-    }
-}
-
-@mixin pc-fix-scrollbar-white(){
-    &::-webkit-scrollbar {
-        width: 5px;
-        height: 5px;
-    }
-    &::-webkit-scrollbar-thumb {
-        background: #d6d6d6;
-        opacity: .7;
-        border-radius: 3px;
-
-        &:hover {
-            background: #707070;
-        }
-    }
-    &::-webkit-scrollbar-track {
-        background: transparent;
-    }
-}
-
-.vertical-scroll {
-  overflow-y: scroll;
-
-  @include pc-fix-scrollbar-white();
-}

+ 0 - 1
src/common/ConstStrings.ts

@@ -1 +0,0 @@
-export const NO_CONTENT_STRING = '无内容,请添加内容!';

+ 0 - 98
src/common/ConvertRgeistry.ts

@@ -1,98 +0,0 @@
-import { 
-  DATA_MODEL_ERROR_REQUIRED_KEY_MISSING, 
-  DATA_MODEL_ERROR_TRY_CONVERT_BAD_TYPE, 
-  DATA_MODEL_ERROR_PRINT_SOURCE,
-  DATA_MODEL_ERROR_ARRAY_REQUIRED_KEY_MISSING,
-  DATA_MODEL_ERROR_ARRAY_IS_NOT_ARRAY,
-  DATA_MODEL_ERROR_MUST_PROVIDE_SIDE,
-  DATA_MODEL_ERROR_REQUIRED_KEY_NULL,
-  DataConverter, 
-  DataErrorFormatUtils, 
-  defaultDataErrorFormatHandler 
-} from "@imengyu/js-request-transform";
-
-function setErrorFormatter() {
-  DataErrorFormatUtils.setFormatHandler((error, data) => {
-    switch (error) {
-      case DATA_MODEL_ERROR_REQUIRED_KEY_MISSING: 
-        return `字段 ${data.sourceKey} 必填但未提供。 来源 ${data.source}; 对象 ${data.objectName} ${data.serverKey ? ('服务器应传字段: ' + data.serverKey) : ''}`;
-      case DATA_MODEL_ERROR_TRY_CONVERT_BAD_TYPE:
-        return `尝试将 ${data.sourceType} 转换为 ${data.targetType}。`;   
-      case DATA_MODEL_ERROR_PRINT_SOURCE:
-        return `来源: ${data.objectName}.`;
-      case DATA_MODEL_ERROR_ARRAY_REQUIRED_KEY_MISSING:
-        return `转换数组模型失败: 需要的字段 ${data.sourceKey} 未提供。`
-      case DATA_MODEL_ERROR_ARRAY_IS_NOT_ARRAY:
-        return `转换数组模型失败: 需要的字段 ${data.sourceKey} 不是数组类型。`
-      case DATA_MODEL_ERROR_MUST_PROVIDE_SIDE:
-        return `转换字段 ${data.key} 失败: 必须提供 ${data.direction} 侧数据。`;
-      case DATA_MODEL_ERROR_REQUIRED_KEY_NULL:
-        return `转换字段 ${data.key} 失败: 必填字段 ${data.key} 未提供或者为 null。`;
-    }
-    return defaultDataErrorFormatHandler(error, data);
-  });
-}
-
-export function registryConvert() {
-  setErrorFormatter();
-  DataConverter.registerConverter({
-    key: 'SplitCommaArray',
-    targetType: 'splitCommaArray',
-    converter: (source, key, type) => {
-      if (typeof source === 'string') 
-        return {
-          success: true,
-          result: source?.split(',') || [],
-        }
-      return {
-        success: false,
-        convertFailMessage: `[${key}] 不是字符串类型`,
-      };
-    }
-  })
-  DataConverter.registerConverter({
-    key: 'CommaArrayMerge',
-    targetType: 'commaArrayMerge',
-    converter: (source, key, type) => {
-      if (source instanceof Array) 
-        return {
-          success: true,
-          result: source?.join(',') || '',
-        }
-      return {
-        success: false,
-        convertFailMessage: `[${key}] 不是数组类型`,
-      };
-    }
-  })
-  DataConverter.registerConverter({
-    key: 'ForceArray',
-    targetType: 'forceArray',
-    converter: (source, key, type) => {
-      if (source instanceof Array) 
-        return {
-          success: true,
-          result: source,
-        }
-      if (typeof source === 'object' && source !== null) {
-        const arr = []
-        for (const key in source) {
-          arr.push((source as Record<string, any>)[key])
-        }
-        return {
-          success: true,
-          result: arr,
-        }
-      }
-      if (typeof source === 'string')
-        return {
-          success: true,
-          result: source.split(','), 
-        }
-      return {
-        success: false,
-        convertFailMessage: `[${key}] 不是数组类型`,
-      };
-    }
-  })
-}

+ 0 - 8
src/common/EventBus.ts

@@ -1,8 +0,0 @@
-import mitt from 'mitt'
-
-export type EventBusOnPageBackData = { name: string, data: any }
-type Events = {
-  pageActionListenOnPageBack: EventBusOnPageBackData,
-}
-
-export const EventBus = mitt<Events>();

+ 0 - 9
src/common/config/ApiCofig.ts

@@ -1,9 +0,0 @@
-
-/**
- * 说明:后端接口配置
- */
-export default {
-  serverDev: 'https://mn.wenlvti.net/api',
-  serverProd: 'https://mn.wenlvti.net/api',
-  mainBodyId: 1,
-}

+ 0 - 11
src/common/config/AppCofig.ts

@@ -1,11 +0,0 @@
-
-/**
- * 说明:应用静态配置
- */
-export default {
-  version: '0.0.1',
-}
-/**
- * 是否是开发环境
- */
-export const isDev = import.meta.env.DEV;

+ 0 - 55
src/components/Footer.vue

@@ -1,55 +0,0 @@
-<template>
-  <!-- 底部 -->
-  <footer>
-    <div class="container">
-      <div class="footer-select">
-        <select name="related-links" id="related-links" @change="handleLinkChange">
-          <option value="">相关网站链接</option>
-          <option value="http://www.ncha.gov.cn/index.html">国家文物局</option>
-          <option value="https://wlt.fujian.gov.cn/">福建省文化和旅游厅</option>
-          <option value="https://www.xm.gov.cn/">厦门市人民政府</option>
-        </select>
-      </div>
-      
-      <div class="footer-links">
-        <a v-for="link in footerInfo.links" :key="link.url" :href="link.url" class="footer-link">{{ link.name }}</a>
-      </div>
-      
-      <div class="footer-info">
-        <p>闽公网安备35020302036685号 | <a href="https://beian.miit.gov.cn/">闽ICP备2025115303号-1</a> | 
-          <!-- <script type="text/javascript">document.write(unescape("%3Cspan id='_ideConac' %3E%3C/span%3E%3Cscript src='https://dcs.conac.cn/js/14/215/0000/60539030/CA142150000605390300003.js' type='text/javascript'%3E%3C/script%3E"));</script> -->
-        </p>
-        <p>联系地址: {{ footerInfo.address }}</p>
-        <p>联系电话: {{ footerInfo.contactPhone }} | 工作电话: {{ footerInfo.workPhone }}</p>
-      </div>
-    </div>
-  </footer>
-</template>
-
-<script setup lang="ts">
-import { ref } from 'vue';
-
-// 底部信息
-const footerInfo = ref({
-  contactPhone: "0592-5024292",
-  workPhone: "0592-5045291",
-  address: "厦门市思明区体育路95号 邮编:361012",
-  links: [
-    { name: "关于我们", url: "#" },
-    { name: "联系我们", url: "#" },
-    { name: "网站声明", url: "#" },
-    { name: "隐私声明", url: "#" },
-    { name: "使用帮助", url: "#" },
-    { name: "网站地图", url: "#" }
-  ]
-});
-
-// 相关网站链接选择框事件处理
-function handleLinkChange(event: Event) {
-  const selectElement = event.target as HTMLSelectElement;
-  const selectedValue = selectElement.value;
-  if (selectedValue) {
-    window.location.href = selectedValue;
-  }
-}
-</script>

+ 0 - 64
src/components/NavBar.vue

@@ -1,64 +0,0 @@
-<template>
-  <!-- 头部 -->
-  <header>
-    <div class="inner">
-      <div class="logo">厦门市文化遗产保护中心</div>
-      <!-- 导航 -->
-      <nav class="main-nav" :class="{ show: isMenuOpen }">
-        <div class="container">
-          <ul class="nav-list">
-            <li v-for="item in navItems.content.value" :key="item.url" class="nav-item" :class="{ active: isActive(item.url) }">
-              <router-link :to="item.url">{{ item.name }}</router-link>
-            </li>
-          </ul>
-        </div>
-      </nav>
-      <div class="d-flex flex-row align-items-center">
-        <button class="mobile-menu-toggle" @click="toggleMenu"><Icon name="material-symbols:menu" /></button>
-        <div class="search-bar">
-          <button @click="toggleSearch"><Icon name="material-symbols:search-rounded" /></button>
-          <input type="text" placeholder="搜索..." v-model="keyword" @blur="hideSearch" @keydown.enter="goSearch" :class="{ show: isSearchOpen }">
-        </div>
-      </div>
-    </div>
-  </header>
-</template>
-
-<script setup lang="ts">
-import { ref } from 'vue';
-import { useRoute } from 'vue-router';
-import { useSSrSimpleDataLoader } from '~/composeable/SimpleDataLoader';
-
-const route = useRoute();
-
-const navItems = await useSSrSimpleDataLoader('navItems', async () => {
-  const data = (await $fetch('/api/channel/nav')).data || [];
-  return data.map(item => {
-    return {
-      ...item,
-      url: item.type === 'list' ? `/channel/${item.diyname}` : item.outlink,
-    }
-  });
-});
-const isMenuOpen = ref(false);
-const isSearchOpen = ref(false);
-const keyword = ref('');
-
-function isActive(url: string): boolean {
-  return route.path.startsWith(url);
-}
-function toggleMenu() {
-  isMenuOpen.value = !isMenuOpen.value;
-}
-function toggleSearch() {
-  isSearchOpen.value = !isSearchOpen.value;
-}
-function hideSearch() {
-  isSearchOpen.value = false;
-}
-function goSearch() {
-  if (keyword.value.trim()) {
-    window.location.href = `/search/?keyword=${encodeURIComponent(keyword.value.trim())}`;
-  }
-}
-</script>

+ 0 - 80
src/components/content/SimplePageContentLoader.vue

@@ -1,80 +0,0 @@
-<template>
-  <div
-    v-if="loader?.loadStatus.value == 'loading'"
-    style="min-height: 200rpx;display: flex;justify-content: center;align-items: center;"
-  >
-    <a-spin tip="加载中" />
-  </div>
-  <div
-    v-else-if="loader?.loadStatus.value == 'error'"
-    style="min-height: 200rpx"
-  >
-    <a-empty :description="loader.loadError.value" >
-      <a-button  @click="handleRetry">重试</a-button>
-    </a-empty>
-  </div>
-  <template v-else-if="loader?.loadStatus.value == 'finished' || loader?.loadStatus.value == 'nomore'">
-    <slot />
-  </template>
-  <div
-    v-if="showEmpty || loader?.loadStatus.value == 'nomore'"
-    style="min-height: 200rpx"
-    class="empty"
-  >
-    <a-empty :description="emptyView?.text ?? '暂无数据'">
-      <a-button
-        v-if="emptyView?.button"
-        @click="emptyView?.buttonClick ?? handleRetry"
-      >
-        {{emptyView?.buttonText ?? '刷新'}}
-      </a-button>
-    </a-empty>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { onMounted, ref, type PropType } from 'vue';
-import type { ILoaderCommon } from '../../composeable/LoaderCommon';
-
-const props = defineProps({	
-  loader: {
-    type: Object as PropType<ILoaderCommon<any>>,
-    default: null,
-  },
-  autoLoad: {
-    type: Boolean,
-    default: false, 
-  },
-  showEmpty: {
-    type: Boolean,
-    default: false, 
-  },
-  emptyView: {
-    type: Object as PropType<{
-      text: string,
-      buttonText: string,
-      button: boolean,
-      buttonClick: () => void,
-    }>,
-    default: null,
-  },
-})
-
-const loaded = ref(false);
-
-onMounted(() => {
-  loaded.value = false;
-  if (props.autoLoad)
-    handleLoad(); 
-});
-
-function handleRetry() {
-  props.loader.loadData(undefined);
-}
-function handleLoad() {
-  if (loaded.value) 
-    return;
-  loaded.value = true;
-  props.loader.loadData(undefined);
-}
-</script>

+ 0 - 9
src/composeable/LoaderCommon.ts

@@ -1,9 +0,0 @@
-import type { Ref } from "vue";
-
-export type LoaderLoadType = 'loading' | 'finished' | 'nomore' | 'error';
-
-export interface ILoaderCommon<P> {
-  loadError: Ref<string>;
-  loadStatus: Ref<LoaderLoadType>;
-  loadData: (params?: P, refresh?: boolean) => Promise<void>;
-}

+ 0 - 103
src/composeable/PageAction.ts

@@ -1,103 +0,0 @@
-import { EventBus } from "@/common/EventBus";
-import { onBeforeUnmount, onMounted, type App } from "vue";
-import { useRoute, useRouter, type LocationQueryRaw } from "vue-router";
-
-/**
- * 说明:页面导航相关函数封装。
- */
-
-type OnPageBackCb = (data: Record<string, unknown>) => void
-
-const EventBusName = 'pageActionListenOnPageBack';
-
-export function useOnPageBack() {
-
-  const route = useRoute();
-  const cbs : OnPageBackCb[]  = [] 
-
-  function onPageBack(cb: OnPageBackCb) {
-    cbs.push(cb)
-  }
-
-  onMounted(() => {
-    EventBus.on(EventBusName, (e) => {
-      if (e.name === route.name) {
-        cbs.forEach((cb) => {
-          cb(e.data);
-        })
-      }
-    });
-  });
-  onBeforeUnmount(() => {
-    EventBus.off(EventBusName);
-  });
-
-  return {
-    onPageBack,
-  }
-}
-
-export function usePageAction() {
-  const router = useRouter();
-
-  /**
-   * 页面跳转: 后退至上一个页面。
-   */
-  function back() {
-    router.back();
-  }
-  /**
-   * 页面跳转: 后退并返回数据至上一个页面的 onPageBack 方法。
-   * @param data 要返回的数据
-   */
-  function backReturnData(data: Record<string, unknown>) {
-    callPrevOnPageBack('' + router.options.history.state.back, data);
-    router.back();
-  }
-  /**
-   * 页面跳转: 跳转到指定页面
-   * @param url 页面路径
-   * @param data 要传递的数据
-   */
-  function navTo(url: string, data: Record<string, unknown> = {}) {
-    const data2 : LocationQueryRaw = {}
-
-    for (const key in data) {
-      if (Object.prototype.hasOwnProperty.call(data, key))
-        data2[key] = data[key] as string;
-    }
-
-    router.push({
-      path: url,
-      query: data2,
-    });
-  }
-  /**
-   * 页面数据传递: 调用上一个页面的 onPageBack 方法
-   * @param name 方法名
-   * @param data 要传递的数据
-   */
-  function callPrevOnPageBack(name: string, data: Record<string, unknown>) {
-    EventBus.emit(EventBusName, {
-      name,
-      data,
-    })
-  }
-  /**
-   * 页面跳转: 调用上一个页面的 onPageBack 方法并返回至上一个页面
-   * @param name 方法名
-   * @param data 要传递的数据
-   */
-  function backAndCallOnPageBack(name: string, data: Record<string, unknown>) {
-    router.back();
-    callPrevOnPageBack(name, data);
-  }
- 
-  return {
-    back,
-    backReturnData,
-    backAndCallOnPageBack, 
-    navTo,
-    callPrevOnPageBack,
-  }
-}

+ 0 - 38
src/composeable/PageQuerys.ts

@@ -1,38 +0,0 @@
-import { nextTick, onMounted, ref, watch, type Ref } from "vue";
-import { useRoute } from "vue-router";
-
-export function useLoadQuerys<T extends Record<string, any>>(
-  defaults: T, 
-  afterLoad?: (querys: T) => void
-) {
-
-  const querys = ref<T>(defaults) as Ref<T>; 
-  const route = useRoute();
-
-  function loadQuerys() {
-    const _querys = route.query;
-    if (_querys) {
-      for (const key in querys.value) {
-        if (typeof defaults[key] === 'number')
-          (querys.value as Record<string, any>)[key] = Number(_querys[key]); 
-        else
-          querys.value[key] = _querys[key] as any;
-      }
-    }
-    afterLoad?.(querys.value);
-  }
-
-  watch(route, () => {
-    loadQuerys();
-  });
-
-  onMounted(() => {
-    nextTick(() => {
-      loadQuerys();
-    });
-  });
-
-  return {
-    querys,
-  }
-}

+ 0 - 109
src/composeable/SimpleDataLoader.ts

@@ -1,109 +0,0 @@
-import { onMounted, ref, type Ref } from "vue";
-import type { ILoaderCommon, LoaderLoadType } from "./LoaderCommon";
-
-export interface ISimpleDataLoader<T, P> extends ILoaderCommon<P> {
-  content: Ref<T|null>;
-  getLastParams: () => P | undefined;
-}
-
-export function useSimpleDataLoader<T, P = any>(
-  loader: (params?: P) => Promise<T>,
-  loadWhenMounted = true,
-  emptyIfArrayEmpty = true,
-)  : ISimpleDataLoader<T, P>
- {
-
-  const content = ref<T|null>(null) as Ref<T|null>;
-  const loadStatus = ref<LoaderLoadType>('loading');
-  const loadError = ref('');
-
-  let lastParams: P | undefined;
-
-  async function loadData(params?: P) {
-    if (params)
-      lastParams = params;
-    loadStatus.value = 'loading';
-    try {
-      const res = (await loader(params ?? lastParams)) as T;
-      content.value = res;
-      if (Array.isArray(res) && emptyIfArrayEmpty && (res as any[]).length === 0)
-        loadStatus.value = 'nomore';
-      else
-        loadStatus.value = 'finished';
-      loadError.value = '';
-    } catch(e) {
-      loadError.value = '' + e;
-      loadStatus.value = 'error';
-      console.log(e);
-      
-    }
-  }
-
-  onMounted(() => {
-    if (loadWhenMounted) {
-      setTimeout(() => {
-        loadData();
-      }, (0.5 + Math.random()) * 500);
-    }
-  })
-
-  return {
-    content,
-    loadStatus,
-    loadError,
-    loadData,
-    getLastParams: () => lastParams,
-  }
-}
-
-export async function useSSrSimpleDataLoader<T, P = any>(
-  name: string,
-  loader: (params?: P) => Promise<T>,
-  params : P|undefined = undefined,
-  emptyIfArrayEmpty = true,
-)  : Promise<ISimpleDataLoader<T, P>>
- {
-  const route = useRoute();
-
-  let lastParams: P | undefined = params;
-  const loadStatus = ref<LoaderLoadType>('finished');
-  const loadError = ref('');
-  const { data: content, error } = (await useAsyncData(route.fullPath + '/' + name, () => loader(lastParams)))
-
-
-  async function loadData(params?: P, refresh: boolean = false) {
-    if (!import.meta.client)
-      return;
-    if (params)
-      lastParams = params;
-    loadStatus.value = 'loading';
-    try {
-      const res = await loader(params ?? lastParams) as T;
-      content.value = res as any;
-      if (Array.isArray(res) && emptyIfArrayEmpty && (res as any[]).length === 0)
-        loadStatus.value = 'nomore';
-      else
-        loadStatus.value = 'finished';
-      loadError.value = '';
-    } catch(e) {
-      loadError.value = '' + e;
-      loadStatus.value = 'error';
-      console.log(e);
-    }
-  }
-
-  watch(error, (e) => {
-    if (e) {
-      loadError.value = '' + e;
-      loadStatus.value = 'error';
-    }
-  }, { immediate: true });
-  
-  return {
-    content: content as Ref<T|null>,
-    loadStatus,
-    loadError,
-    loadData,
-    getLastParams: () => lastParams,
-  }
-}

+ 0 - 232
src/composeable/SimplePagerDataLoader.ts

@@ -1,232 +0,0 @@
-import { watch, ref, computed, type Ref } from "vue"
-import type { ILoaderCommon, LoaderLoadType } from "./LoaderCommon";
-
-export interface ISimplePageListLoader<T, P> extends ILoaderCommon<P> {
-  list: Ref<T[]>;
-  page: Ref<number>;
-  next: () => Promise<void>;
-  prev: () => Promise<void>;
-  total: Ref<number>;
-  totalPages: Ref<number>;
-}
-
-/**
- * 简单分页数据封装。
- * 
- * 该封装了分页数据的加载、分页、上一页、下一页等功能。当页码发生变化时,会自动调用加载函数。
- * 简单分页同时只能显示一页数据,重新加载会覆盖之前的数据。
- * 
- * 使用示例:
- * ```ts
- * const { data, page, total, loading } = useSimplePagerDataLoader(10, async (page, pageSize) => {
- *   const res = await fetch(`/api/data?page=${page}&pageSize=${pageSize}`);
- *   const data = await res.json();  
- *   return {
- *     data,
- *     page: res.page,
- *     total: res.total,
- *   };
- * });
- * ```
- *
- * @param pageSize 一页的数量
- * @param loader 加载函数
- * @returns 
- */
-export function useSimplePagerDataLoader<T, P = any>(
-  pageSize: number|Ref<number>, 
-  loader: (page: number, pageSize: number, params?: P) => Promise<{
-    data: T[],
-    total: number,
-  }>)  : ISimplePageListLoader<T, P>
-{
-  const page = ref(0);
-  const list = ref<T[]>([]) as Ref<T[]>;
-  const total = ref(0);
-  const totalPages = computed(() => Math.ceil(total.value / getPageSize()));
-  const loadStatus = ref<LoaderLoadType>('loading');
-  const loadError = ref('');
-
-  function getPageSize() {
-    return typeof pageSize == 'object'? pageSize.value : pageSize;
-  }
-  
-  watch(page, async () => {
-    await loadData(lastParams, false);
-  });
-
-  let lastParams: P | undefined;
-  let loading = false;
-
-  async function loadData(params?: P, refresh: boolean = false) {
-    if (loading) 
-      return;
-    if (params)
-      lastParams = params;
-    if (refresh) {
-      page.value = 1;
-    }
-    list.value = []; 
-    loadStatus.value = 'loading';
-    loading = true;
-
-    try {
-      const res = (await loader(page.value, getPageSize(), lastParams));
-      list.value = list.value.concat(res.data);
-      total.value = res.total;
-      loadStatus.value = res.data.length > 0 ? 'finished' : 'nomore';
-      loadError.value = '';
-      loading = false;
-    } catch(e) {
-      loadError.value = '' + e;
-      loadStatus.value = 'error';
-      loading = false;
-    }
-  }
-  /**
-   * 下一页
-   */
-  async function next() {
-    if (page.value > total.value)
-      return;
-    page.value++;
-    await loadData(lastParams, false);
-  }
-  /**
-   * 上一页
-   */
-  async function prev() {
-    if (page.value <= 1)
-      return;   
-    page.value--;
-    await loadData(lastParams, false);
-  }
-
-  return {
-    loadData,
-    next,
-    prev,
-    /**
-     * 数据
-     */
-    list,
-    /**
-     * 当前页码
-     */
-    page,
-    /**
-     * 总数据条数
-     */
-    total,
-    /**
-     * 总页数
-     */
-    totalPages,
-    loadError,
-    loadStatus,
-  }
-}
-
-export async function useSSrSimplePagerDataLoader<T, P = any>(
-  name: string,
-  startPage: number,
-  pageSize: number|Ref<number>, 
-  loader: (page: number, pageSize: number, params?: P) => Promise<{
-    data: T[],
-    total: number,
-  }>,
-  params : P|undefined = undefined,
-) : Promise<ISimplePageListLoader<T, P>>
-{
-  const route = useRoute();
-
-  let lastParams: P | undefined = params;
-
-  const page = ref(startPage);
-  const { 
-    data, 
-    error 
-  } = (await useAsyncData(route.fullPath + '/' + name, () => loader(page.value, getPageSize(), lastParams)))
-
-  const list = ref<T[]>([]) as Ref<T[]>;
-  const total = ref(0);
-  const totalPages = computed(() => Math.ceil(total.value / getPageSize()));
-  const loadStatus = ref<LoaderLoadType>('finished');
-  const loadError = ref('');
-
-  if (error.value) {
-    loadError.value = '' + (error.value);
-    loadStatus.value = 'error';
-  } else if (data.value) {
-    list.value = data.value.data as any;
-    total.value = data.value.total as any;
-    loadError.value = '';
-    loadStatus.value = 'finished';
-  }
-
-  function getPageSize() {
-    return typeof pageSize == 'object'? pageSize.value : pageSize;
-  }
-
-  watch(page, async () => {
-    await loadData(lastParams, false);
-  });
-
-  let loading = false;
-
-  async function loadData(params?: P, refresh: boolean = false) {
-    if (loading) 
-      return;
-    if (params)
-      lastParams = params;
-    if (refresh) {
-      page.value = 1;
-    }
-    list.value = []; 
-    loadStatus.value = 'loading';
-    loading = true;
-
-    try {
-      const res = (await loader(page.value, getPageSize(), lastParams));
-      list.value = list.value.concat(res.data);
-      total.value = res.total;
-      loadStatus.value = list.value.length > 0 ? 'finished' : 'nomore';
-      loadError.value = '';
-      loading = false;
-    } catch(e) {
-      loadError.value = '' + e;
-      loadStatus.value = 'error';
-      loading = false;
-    }
-  }
-  /**
-   * 下一页
-   */
-  async function next() {
-    if (page.value > total.value)
-      return;
-    page.value++;
-    await loadData(lastParams, false);
-  }
-  /**
-   * 上一页
-   */
-  async function prev() {
-    if (page.value <= 1)
-      return;   
-    page.value--;
-    await loadData(lastParams, false);
-  }
-
-  return {
-    loadData,
-    next,
-    prev,
-    list: list as any as Ref<T[]>,
-    page,
-    total,
-    totalPages,
-    loadError,
-    loadStatus,
-  }
-}

+ 0 - 44
src/pages/404.vue

@@ -1,44 +0,0 @@
-<template>
-  <div>
-    <div class="nav-placeholder"></div>
-    <div class="empty-page">
-      <img src="@/assets/images/404.svg" />
-      <h1>抱歉,您访问的页面不存在</h1>
-      <NuxtLink to="/">点击这里返回上一页</NuxtLink>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-</script>
-
-<style lang="scss">
-.empty-page {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  text-align: center;
-  font-family: SourceHanSerifCNBold;
-  min-height: 66vh;
-
-  img {
-    width: 150px;
-    height: 150px;
-    margin-bottom: 20px;
-  }
-  h1 {
-    font-size: 2rem;
-  }
-  a {
-    text-decoration: none;
-    font-size: 1.2rem;
-    cursor: pointer;
-    color: #333;
-
-    &:hover {
-      color: var(--color-primary);
-    }
-  }
-}
-</style>

+ 0 - 107
src/pages/about.vue

@@ -1,107 +0,0 @@
-<template>
-  <!-- 联系我们 -->
-  <div class="main-background">
-    <!-- SEO -->
-    <Head>
-      <Title>厦门市文化遗产保护中心 - 联系我们</Title>
-      <Meta name="description" content="" />
-      <Meta name="keywords" content="" />
-    </Head>
-
-    <!-- 轮播图 -->
-    <img class="main-header-image" src="http://xmswhycbhzx.cn/uploads/20251013/47d9f2bcd627ab15d4e5014b50166440.jpg">
-
-    <!-- 主要内容 -->
-    <div class="main-content">
-      <div class="container">
-        <div class="section-title center large mt-4">
-          <h2 class="icon">联系方式</h2>
-        </div>
-        <div class="row">
-          <div class="col col-sm-12 col-md-12 col-lg-6">
-            
-            <ul class="info-list">
-              <li class="info-item">
-                <i class="fa fa-phone"></i>
-                电话: 0592-2085737
-              </li>
-              <li class="info-item">
-                <i class="fa fa-envelope"></i>
-                邮箱: 734381242@qq.com
-              </li>
-              <li class="info-item">
-                <i class="fa fa-fax"></i>
-                传真: 0592-2090633
-              </li>
-              <li class="info-item">
-                <i class="fa fa-clock"></i>
-                办公时间: 周一至周五 8:00-17:00
-              </li>
-              <li class="info-item">
-                <i class="fa fa-map-marker"></i>
-                地址: 厦门市思明区体育路95号文化艺术中心共享楼
-              </li>
-            </ul>
-          </div>
-          <div class="col col-sm-12 col-md-12 col-lg-6">
-            <div id="map-container" style="width:100%; height:200px;"></div>
-          </div>
-        </div>
-        <div class="section-title center large mt-5">
-          <h2 class="icon">关于我们</h2>
-        </div>
-        <div class="row">
-          <div class="col col-sm-12 col-md-6 col-lg-8">
-            <h3>厦门市文化遗产保护中心</h3>
-            <p>厦门市文化遗产保护中心(厦门市非物质文化遗产保护中心、厦门市闽南文化生态保护中心)为全额拨款公益一类事业单位,隶属厦门市文化和旅游局,机构规格为副处级,内设办公室、文物保护部、非遗保护部、考古部、信息科技部、安全管理部。</p>
-          </div>
-          <div class="col col-sm-12 col-md-6 col-lg-4 d-flex flex-row justify-content-end">
-            <img src="@/assets/images/about-logo.png" alt="厦门市文化遗产保护中心">
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-
-onMounted(() => {
-  if (import.meta.client)
-    initMap();
-});
-
-// 初始化地图
-function initMap() {
-  setTimeout(() => {
-    // 初始化地图
-    const map = new AMap.Map('map-container', {
-      zoom: 16,
-      center: [118.1086912,24.49078560] // 厦门市中心坐标
-    });
-    // 添加标记
-    const marker = new AMap.Marker({
-      position: [118.10869122,24.490785603],
-      title: '厦门市文化遗产保护中心'
-    });
-    // 将标记添加到地图
-    marker.setMap(map);
-    // 添加信息窗口
-    const infoWindow = new AMap.InfoWindow({
-      content: '<div style="padding: 10px;">厦门市文化遗产保护中心</div>',
-      offset: new AMap.Pixel(0, -30)
-    });
-    // 点击标记时显示信息窗口
-    marker.on('click', function() {
-      infoWindow.open(map, marker.getPosition() as unknown as AMap.Vector2);
-    });
-    // 初始就打开信息窗口
-    infoWindow.open(map, marker.getPosition() as unknown as AMap.Vector2);
-  }, 200);
-}
-</script>
-
-<style lang="scss">
-
-</style>
-

+ 0 - 54
src/pages/channel/[id].vue

@@ -1,54 +0,0 @@
-<template>
-  <!-- 分类 -->
-  <div class="main-background">
-    <!-- SEO -->
-    <Head>
-      <Title>厦门市文化遗产保护中心 - {{ channelName }}</Title>
-      <Meta name="description" content="" />
-      <Meta name="keywords" content="" />
-    </Head>
-    <!-- 轮播 -->
-    <Carousel v-bind="carouselConfig" class="main-header-box small carousel-light">
-      <Slide 
-        v-for="(item, key) in newsData.content.value"
-        :key="key"
-        class="main-header-box small"
-      >
-        <img :src="item.image" />
-      </Slide>
-      <template #addons>
-        <Navigation />
-        <Pagination />
-      </template>
-    </Carousel>
-
-  </div>
-</template>
-
-<script setup lang="ts">
-import { Carousel, Slide, Pagination, Navigation } from 'vue3-carousel'
-import { useSSrSimpleDataLoader } from '@/composeable/SimpleDataLoader';
-
-const carouselConfig = {
-  itemsToShow: 1,
-  wrapAround: true,
-  autoPlay: 5000,
-}
-
-const route = useRoute();
-const channelName = route.params.id;
-
-const newsData = await useSSrSimpleDataLoader('news', async () => {
-  throw new Error('newsData is not implemented');
-});
-
-
-
-
-</script>
-
-<style lang="scss">
-@use "sass:list";
-@use "sass:math";
-</style>
-

+ 0 - 50
src/pages/index.vue

@@ -1,50 +0,0 @@
-<template>
-  <!-- 首页 -->
-  <div class="main-background">
-    <!-- SEO -->
-    <Head>
-      <Title>厦门市文化遗产保护中心</Title>
-      <Meta name="description" content="" />
-      <Meta name="keywords" content="" />
-    </Head>
-    <!-- 轮播 -->
-    <Carousel v-bind="carouselConfig" class="main-header-box small carousel-light">
-      <Slide 
-        v-for="(item, key) in newsData.content.value"
-        :key="key"
-        class="main-header-box small"
-      >
-        <img :src="item.image" />
-      </Slide>
-      <template #addons>
-        <Navigation />
-        <Pagination />
-      </template>
-    </Carousel>
-
-  </div>
-</template>
-
-<script setup lang="ts">
-import { Carousel, Slide, Pagination, Navigation } from 'vue3-carousel'
-import { ref } from 'vue';
-import { useRouter } from 'vue-router';
-import { useSSrSimpleDataLoader } from '@/composeable/SimpleDataLoader';
-import { DataDateUtils } from '@imengyu/js-request-transform';
-import { ScrollRect } from '@imengyu/vue-scroll-rect';
-
-const carouselConfig = {
-  itemsToShow: 1,
-  wrapAround: true,
-  autoPlay: 5000,
-}
-const newsData = await useSSrSimpleDataLoader('news', async () => {
-  throw new Error('newsData is not implemented');
-});
-</script>
-
-<style lang="scss">
-@use "sass:list";
-@use "sass:math";
-</style>
-

+ 0 - 4
src/scripts/.gitignore

@@ -1,4 +0,0 @@
-node_modules
-_config.json
-_token.json
-upload.zip

+ 0 - 22
src/scripts/UpdateScript/deprecate.html

@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="UTF-8" />
-    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>提示</title>
-    <style>.global-error{position:fixed;left:0;right:0;bottom:100px;top:0;display:flex;flex-direction:column;justify-content:center;align-items:center;background-color:#fff;-webkit-app-region:drag}.global-error span{margin-top:20px;font-size:16px}button{margin-top:20px;padding:0;display:inline-block;-webkit-app-region:no-drag;height:40px;width:120px;appearance:none;outline:none;border:none;background-color:#0083da;color:#fff;cursor:pointer}button:hover{background-color:#005c99}</style>
-  </head>
-  <body>
-    <script>
-      function relaunch() {
-        if (typeof api.relaunch == 'function') 
-          api.relaunch();
-        else
-          location.reload();
-      }
-    </script>
-    <div class="global-error">
-      <span>系统已更新,请重启</span>
-      <button id="load-confirm-button" onclick="relaunch()">确定</button>
-    </div>
-</html>

+ 0 - 524
src/scripts/UpdateScript/index.mjs

@@ -1,524 +0,0 @@
-/**
- * 更新发布工具
- * 
- * Copyright © 2025 imengyu.top imengyu-update-server
- */
-
-import { program } from 'commander';
-import { password, input, confirm, select } from '@inquirer/prompts';
-import { writeFile, readFile } from 'node:fs/promises';
-import { postAppUpdate, postWebUpdate } from './postUpdate.mjs';
-import Table from 'cli-table3';
-import md5 from 'md5';
-import axios from 'axios';
-import path from 'path';
-import { dirname } from 'node:path';
-import { fileURLToPath } from 'node:url';
-
-//基础配置
-//========================================
-
-const __filename = fileURLToPath(import.meta.url);
-const __dirname = dirname(__filename);
-const constant = {
-  ServerUrl: 'http://update-server1.imengyu.top/',
-  TokenSave: path.resolve(__dirname, './_token.json'),
-};
-let currentData = {
-  token: '',
-  identifier: '',
-};
-
-readFile(constant.TokenSave).then((res) => {
-  currentData = JSON.parse(res);
-  if (!currentData.identifier) 
-    currentData.identifier = `commandClient${Math.floor(Math.random() * 1000)}`;
-  start();
-}).catch(() => {
-  start();
-})
-
-const axiosInstance = axios.create({
-  baseURL: constant.ServerUrl,
-  timeoutErrorMessage: '请求超时,请检查网络连接',
-  responseType: 'json',
-  withCredentials: false,
-  validateStatus: () => true,
-});
-
-axiosInstance.interceptors.request.use((value) => {
-  value.headers['authorization'] = JSON.stringify({
-    auth: currentData?.token?.authName,
-    validity: currentData?.token?.authKey,
-    nonce: "aaaaaaaaaa",
-    identifier: currentData.identifier,
-    key: 'abc123',
-  });  
-  value.url = value.url + (value.url.includes('?') ? '&' : '?' ) + `identifier=${currentData.identifier}`
-  return value;
-});
-axiosInstance.interceptors.response.use((value) => {
-  if (value.data.success) 
-    return value.data;
-  else
-    return Promise.reject(value.data);
-});
-
-function getErrorMessage(e) {
-  return e instanceof Error ? e.message : (typeof e === 'object' ? e : ('' + e));
-}
-
-//登录相关
-//========================================
-
-async function checkLogged() {
-  try {
-    await axiosInstance.get('/auth');
-    console.log('已登录');
-  } catch (e) {
-    console.error('获取状态失败:', getErrorMessage(e));
-  }
-}
-async function login(user) {
-  try {
-    const pass = await password({ message: '输入密码' });
-    const res = await axiosInstance.post('/auth?rember=true', {
-      method: 'key',
-      key: `${user}@${md5(pass)}`,
-    })
-    currentData.token = {
-      authName: res.data.authName,
-      authKey: res.data.authKey,
-    };
-    writeFile(constant.TokenSave, JSON.stringify(currentData));
-    console.log('登录成功');
-  } catch (e) {
-    console.error('登录失败', getErrorMessage(e));
-  }
-}
-async function logout() {
-  currentToken = '';
-  writeFile(constant.TokenSave, JSON.stringify(currentData));
-  try {
-    await axiosInstance.delete('/auth');
-    console.log('退出登录成功');
-  } catch(e) {
-    console.error('退出登录失败', getErrorMessage(e));
-  }
-}
-
-//版本相关
-//========================================
-
-async function viewVersion(type) {
-  if (type === 'all' || !type) {
-    const data = await axiosInstance.get('/version/list');
-    const table = new Table({
-      head: ['ID', '版本'], 
-      colWidths: [10, 20 ]
-    });
-
-    data.data.forEach((d) => {
-      table.push([ d.id, d.version ]);
-    })
-    console.log(table.toString());
-  } else {
-    let data = null;
-    try {
-      if (Number.isNaN(Number(type)))
-        data = await axiosInstance.get('/version/get-by-name?name=' + type);
-      else
-        data = await axiosInstance.get('/version/' + type);
-    }
-    catch (e) {
-      console.error('Failed to load version info', e);
-      return;
-    }
-    const table = new Table({
-      head: ['key', 'data'], 
-      colWidths: [30, 60]
-    });
-    table.push(
-      [ 'ID', data.data.id ],
-      [ '状态', stateConstant[data.data.status] ],
-      [ '版本号', data.data.version ],
-      [ '创建时间', new Date(data.data.createAt).toString() ],
-      [ '设置', data.data.config ],
-      [ '激活的Web更新ID', data.data.webUpdateId ],
-      [ '激活的App更新ID', data.data.appUpdateId ],
-      [ '激活的下一个App更新ID', data.data.appUpdateNextId ],
-    );
-    console.log(table.toString());
-  }
-}
-async function getVersion(action, type) {
-  switch(action) {
-    case 'view':
-      await viewVersion(type);
-      break;
-    case 'new': {
-      const version = await input({ message: 'Enter version name, like (1.0.0)' });
-      try {
-        await axiosInstance.post('/version', {
-          version: version,
-          status: 1,
-          config: "{}",
-        });
-        console.log('Add version success');
-      } catch (e) {
-        console.error('Failed to add version', e);
-      }
-      break;
-    }
-    case 'delete': {
-      const versionId = type ? type : await input({ message: '输入版本ID' });
-      if (!await confirm({ message: `确定删除版本 ${versionId}?`, default: false }))
-        return;
-      if (!await confirm({ message: '确认删除版本?此操作会删除所属版本的所有更新项目、存储等,无法恢复,是否确定删除?', default: false }))
-        return;
-      try {
-        await axiosInstance.delete('/version/' + versionId);
-        console.log('删除版本成功');
-      } catch (e) {
-        console.error('删除版本失败', e);
-      }
-      break;
-    }
-    case 'set-state': {
-      const versionId = type ? type : await input({ message: '输入版本ID' });
-      const state = await select({
-        message: '设置状态',
-        choices: [
-          {
-            name: 'NotEnable',
-            value: 0,
-          },
-          {
-            name: 'Normal',
-            value: 1,
-          },
-          {
-            name: 'Deprecated',
-            value: 2,
-          },
-        ],
-      });
-
-      try {
-        await axiosInstance.put('/version/' + versionId, {
-          status: state
-        });
-        console.log('设置状态成功');
-      } catch (e) {
-        console.error('设置状态失败', e);
-      }
-      break;
-    }
-    case 'set-config': {
-      const versionId = type ? type : await input({ message: '输入版本ID' });
-      const config = await input({ message: '输入配置Json' });
-      try {
-        await axiosInstance.put('/version/' + versionId, {
-          config: config,
-        });
-        console.log('设置配置成功');
-      } catch (e) {
-        console.error('设置配置失败', e);
-      }
-      break;
-    }
-    case 'set-active-app-update': {
-      const versionId = await input({ message: '输入版本ID' });
-      const updateId = await input({ message: '输入更新ID' });
-      const isNext = await confirm({ message: 'Set as next active?', default: false });
-
-      try {
-        await axiosInstance.post('/update/active/app', { versionId, updateId, isNext });
-        console.log('成功');
-      } catch (e) {
-        console.error('失败', e);
-      }
-      break;
-    }
-    case 'set-active-web-update': {
-      const versionId = await input({ message: '输入版本ID' });
-      const updateId = await input({ message: '输入更新ID' });
-
-      try {
-        await axiosInstance.post('/update/active/web', { versionId, updateId });
-        console.log('成功');
-      } catch (e) {
-        console.error('失败', e);
-      }
-      break;
-    }
-    default:
-      console.error('未知参数', action);
-      break;
-  }
-}
-
-//选择方法
-//========================================
-
-export async function selectVersion(requireString = false, defaultVersionId = null) {
-  const data = (await axiosInstance.get('/version/list')).data;
-  if (data.length === 0) {
-    console.error('没有版本');
-    return;
-  }
-  const resultId = (await select({
-    choices: data.map(p => ({
-      value: p.id,
-      name: p.version,
-    })),
-    default: defaultVersionId,
-    message: '选择一个版本',
-  }));
-  if (requireString) {
-    return data.find(p => p.id === resultId).version
-  }
-  return resultId;
-}
-export async function selectUpdate() {
-  const versionId = await selectVersion();
-  const data = (await axiosInstance.get('/version/update?search=' + JSON.stringify({ versionId }))).data;
-  if (data.length === 0) {
-    console.error('没有更新');
-    return;
-  }
-  const resultId = (await select({
-    choices: data.map(p => ({
-      value: p.id,
-      name: p.version,
-    })),
-    default: defaultVersionId,
-    message: '选择一个更新',
-  }));
-  return resultId;
-}
-
-//更新相关
-//========================================
-
-const stateConstant = [ 'Deleted', 'Normal', 'Deprecated' ];
-const typeConstant = [ 'Unknow', 'Web', 'app' ];
-const storageTypeConstant = [ 'Unknow', 'LocalStorage', 'AliOSS' ];
-
-async function postUpdate(type, options) {
-  switch (type) {
-    case 'web': {
-      await postWebUpdate(axiosInstance, options);
-      break;
-    }
-    case 'app': {
-      await postAppUpdate(axiosInstance, options);
-      break;
-    }
-    default:
-      console.error('Unknow type', type);
-      break;
-  }
-}
-async function deprecateOrDeleteUpdate(updateId) {
-  if (!updateId)
-    updateId = await selectUpdate();
-
-  const { currentUpdateInfo, currentVersionInfo } = await viewUpdate(updateId);
-
-  const deprecate = (await select({
-    choices: [
-      {
-        name: 'Deprecate',
-        value: 0,
-      },
-      {
-        name: 'Delete',
-        value: 1,
-      },
-    ],
-    message: '删除或弃用?',
-  })) === 0;
-
-  if (deprecate && currentUpdateInfo.type !== 1) {
-    console.log('只有Web更新允许弃用');
-    return;
-  }
-  if (currentUpdateInfo.status === 0) {
-    console.log(`当前状态 ${stateConstant[currentUpdateInfo.status]} 无法弃用`);
-    return;
-  }
-
-  if (deprecate) {
-    if (!await confirm({ message: `确定弃用当前版本 ${currentUpdateInfo.versionCode} ?弃用会删除存储文件以及备份。此操作无法恢复!`, default: false }))
-      return;
-  } else {
-    if (!await confirm({ message: `确定删除当前版本 ${currentUpdateInfo.versionCode} ?此操作无法恢复!`, default: false }))
-      return;
-  }
-
-  try {
-    await axiosInstance.post(`/update/${deprecate ? 'deprecate' : 'delete'}`, { updateId });
-    console.log(`${deprecate ? '弃用' : '删除'} 成功`);
-  } catch (e) {
-    console.error(`操作失败`, e);
-  }
-}
-async function viewUpdate(updateId) {
-  
-  let currentUpdateInfo = null
-  let currentVersionInfo = null
-
-  try {
-    currentUpdateInfo = (await axiosInstance.get('/update/' + updateId)).data;
-  } catch (e) {
-    console.error('加载更新信息失败', updateId);
-  }
-  try {
-    currentVersionInfo = (await axiosInstance.get('/version/' + currentUpdateInfo.versionId)).data;
-  } catch (e) {
-    console.error('加载版本信息失败', currentUpdateInfo.versionId);
-  }
-
-  const table = new Table({
-    head: ['key', 'data'], 
-    colWidths: [20, 40]
-  });
-  table.push(
-    [ 'ID', currentUpdateInfo.id ],
-    [ '所属应用', currentUpdateInfo.name ],
-    [ '更新信息', currentUpdateInfo.updateInfo ],
-    [ '版本号', currentUpdateInfo.versionCode ],
-    [ '创建时间', new Date(currentUpdateInfo.createAt).toString() ],
-    [ '类型', typeConstant[currentUpdateInfo.type] ],
-    [ '状态', stateConstant[currentUpdateInfo.status] ],
-    [ '强制更新', currentUpdateInfo.force ],
-    [ '公共访问路径', currentUpdateInfo.publicUrl ],
-    [ '存储类型', storageTypeConstant[currentUpdateInfo.storageType] ],
-    [ '存储路径', currentUpdateInfo.storagePath ],
-  );
-
-  console.log(table.toString());
-
-  if (currentUpdateInfo.activeWebVersionName) {
-    table.push(
-      [ '使用中的Web版本', currentUpdateInfo.activeWebVersionName ],
-    );
-  }
-  if (currentUpdateInfo.activeAppVersionName) {
-    table.push(
-      [ '使用中的App版本', currentUpdateInfo.activeAppVersionName ],
-    );
-  }
-
-  return {
-    currentUpdateInfo,
-    currentVersionInfo,
-  }
-}
-async function getUpdate(action, type, all, options) {
-  const typeNotANumber = isNaN(new Number(type)); 
-
-  if (action === 'view' && (!type || typeNotANumber)) {
-
-    let hasSerch = false;
-    const search = {};
-    const sort = {
-      field: "createAt",
-      order: "descend"
-    }
-    if (typeNotANumber && type !== 'all') {
-      hasSerch = true;
-      search.version = type;
-    }
-
-    const data = await axiosInstance.get('/update/list?full=true' + (hasSerch ? ('&search=' + JSON.stringify(search)) : '') + '&sort=' + JSON.stringify(sort));
-    const table = new Table({
-      head: ['ID', '版本', '版本号', '类型', '状态'], 
-      colWidths: [10, 10, 20, 10, 15 ]
-    });
-
-    if (type !== 'all' && all !== 'all' && data.data.length > 10) {
-      data.data = data.data.slice(0, 10);
-      console.log('filter!', all);
-    }
-
-    data.data.forEach((d) => {
-      table.push([ 
-        d.id, 
-        (d.activeAppVersionName ? `${d.activeAppVersionName} (App)` : (
-          d.activeWebVersionName? `${d.activeWebVersionName} (Web)` : '无'
-        )),  
-        d.versionCode, typeConstant[d.type], stateConstant[d.status] 
-      ]);
-    })
-    
-    console.log(table.toString());
-  } else {
-    switch (action) {
-      case 'post': {
-        await postUpdate(type, options);
-        break;
-      }
-      case 'delete': {
-        await deprecateOrDeleteUpdate(type);
-        break;
-      }
-      case 'view': {
-        viewUpdate(type);
-        break;
-      }
-      default: 
-        console.error('Unknow action', action);
-        break;
-    }
-  }
-}
-async function testVersion(version) {
-  try {
-    const data = await axiosInstance.get('/update-get-info?version=' + version);
-    console.log('版本信息', data.data);
-  } catch(e) {
-    console.log('获取失败', e);
-  }
-}
-
-//程序入口
-//============================================
-
-program
-  .command('login <user>')
-  .description('登录')
-  .action(login);
-program
-  .command('logstate')
-  .description('检查登录状态')
-  .action(checkLogged);
-program
-  .command('logout')
-  .description('退出登录')
-  .action(logout);
-
-program
-  .command('test <version>')
-  .description('测试更新入口')
-  .action(testVersion);
-
-program
-  .command('version <action> [type]')
-  .description('查看版本信息/发布版本/删除版本/设置版本, action 可选 view/new/delete/set-web-update/set-app-update/set-next-app-update')
-  .action(getVersion);
-program
-  .command('update <action> [type] [all]')
-  .description('查看更新信息/发布更新, action 可选 view/post, view type 可选 id/all; post type 可选 web/app')
-  .option('--skip', '跳过构建')
-  .option('--ndelete', '不删除构建文件')
-  .action(getUpdate);
-
-function start() {
-  program.parse(process.argv);
-}
-
-process.on('unhandledRejection', (reason, p) => {
-  console.error('Promise: ', p, 'Reason: ', reason)
-})

+ 0 - 66
src/scripts/UpdateScript/postConfig.mjs

@@ -1,66 +0,0 @@
-import { readFileSync } from 'node:fs';
-import { pad } from './postUpdate.mjs';;
-import { dirname } from 'node:path';
-import { fileURLToPath } from 'node:url';
-import path from 'node:path';
-
-//基础配置
-//========================================
-
-const __filename = fileURLToPath(import.meta.url);
-const __dirname = dirname(__filename);
-
-//提交更新配置
-//========================================
-
-export const config = {
-  submitKey: '',
-  uploadWebConfig: {
-    storageAction: 'override',
-    storageProps: {
-      overrideMode: 'overrideAll',
-      overrideFiles: [ ],
-      newFolderNameGenerateType: 'hash',
-    },
-    deprecateConfig: {
-      indexFile: 'index.html',
-      updateHtml: readFileSync(path.resolve(__dirname, './deprecate.html'), 'utf8')
-    },
-  },
-  buildWebCommand: 'npm run nuxt-build', //构建命令
-  buildWebOutDir: '../../../.output', //构建输出目录。相对于当前文件目录
-  buildWebOptions: {
-    skipFiles: [
-      '/server/node_modules',
-    ],
-  },
-  buildWebOutVersionPath: '', //版本号输出目录,输出版本号至文件以供项目使用。相对于当前文件目录
-  /**
-   * 自定义生成Web版本号的方法。
-   * @param {Date} now 当前日期
-   * @param {Number} lastTodaySubVersion 今天上传的之前版本数量
-   * @returns 
-   */
-  buildWebVersionGenerateCommand: async (now, lastTodaySubVersion) => {
-    //生成Web版本号
-    const version = `${now.getFullYear().toString().substring(2)}${pad(now.getMonth() + 1, 2)}${pad(now.getDate(), 2)}.${pad(lastTodaySubVersion, 2)}`;
-    return version;
-  },
-  buildAppCallback: async (param, versionCode, lastTodaySubVersion) => {
-    //构建App
-    throw new Error('未实现buildAppCallback方法');
-  }, 
-  buildAppGetUploadFile: async (param) => {
-    //获取上传文件路径
-    throw new Error('未实现buildAppGetUploadFile方法');
-  }, 
-  buildAppGetOSSFileName: async (param) => {
-    //生成OSS保存路径
-    throw new Error('buildAppGetOSSFileName');
-  },//构建命令
-  buildAppOutDir: './dist', //构建输出目录。相对于当前文件目录
-  buildAppGetVersion: async () => {
-    //获取版本号
-    throw new Error('未实现buildAppGetVersion方法');
-  },
-} 

+ 0 - 430
src/scripts/UpdateScript/postUpdate.mjs

@@ -1,430 +0,0 @@
-/**
- * 更新发布工具
- * 
- * Copyright © 2025 imengyu.top imengyu-update-server
- */
-
-import { confirm , input } from '@inquirer/prompts';
-import { writeFile, readFile, access, unlink, readdir, stat, constants } from 'node:fs/promises';
-import { exec } from 'node:child_process';
-import fs from 'fs';
-import archiver from 'archiver';
-import path, { resolve } from 'node:path';
-import OSS from 'ali-oss';
-import cliProgress from 'cli-progress';
-import { selectVersion } from './index.mjs';
-import { config } from './postConfig.mjs';
-import { dirname } from 'node:path';
-import { fileURLToPath } from 'node:url';
-
-//基础配置
-//========================================
-
-const __filename = fileURLToPath(import.meta.url);
-const __dirname = dirname(__filename);
-
-function readFileRange(file, start, length) {
-  return new Promise((resolve, reject) => {
-    fs.open(file, 'r', (err, fd) => {
-      if (err) {
-        reject('Error opening file:', er);
-        return;
-      }
-      const buffer = Buffer.alloc(length);
-      fs.read(fd, buffer, 0, length, start, (err, bytesRead, buffer) => {
-        if (err) {
-          reject('Error reading file:', err)
-          return;
-        }
-        fs.close(fd, (err) => {
-          if (err) {
-            reject('Error closing file:', err)
-            return ;
-          }
-          resolve(buffer);
-        });
-      });
-    });
-  })
-  
-}
-export function pad(num, n) {
-  var strNum = num.toString();
-  var len = strNum.length;
-  while (len < n) {
-    strNum = "0" + strNum;
-    len++;
-  }
-  return strNum;
-}
-async function getConfig() {
-  let postConfig = {
-    lastVersion: '',
-    lastUpdateInfo: '',
-    lastSubmitDay: '',
-    lastTodaySubVersion: 0,
-  };
-  try {
-    postConfig = JSON.parse(await readFile(path.resolve(__dirname, './_config.json')));
-  } catch {
-    //
-  }
-  if (postConfig.lastSubmitDay != new Date().getDate()) 
-    postConfig.lastTodaySubVersion = 0;
-  return postConfig;
-}
-async function saveConfig(postConfig) {
-  postConfig.lastSubmitDay = new Date().getDate();
-  await writeFile(path.resolve(__dirname, './_config.json'), JSON.stringify(postConfig));
-}
-
-async function getUpdateInfo(postConfig) {
-  let updateInfo = await input({ message: '输入更新信息', default: postConfig.lastUpdateInfo });
-  if (updateInfo === 'git') {
-    console.log('开始获取git提交信息');
-    updateInfo = await new Promise((resolve, reject) => {
-      exec('git log -1 --pretty=format:"%h %s"', (error, stdout, stderr) => {
-        if (error) {
-          reject(error);
-          return;
-        }
-        resolve(stdout);
-      });
-    });
-    console.log('使用git提交信息作为更新信息');
-  }
-  return updateInfo;
-}
-
-//App更新与提交
-//========================================
-
-export async function postAppUpdate(axiosInstance, param) {
-  const postConfig = await getConfig();
-  const versionId = await selectVersion(false, postConfig.lastVersion);
-  const updateInfo = await getUpdateInfo(postConfig);
-
-  await axiosInstance.post('/update-post', { config: { type: 2, test: true, versionId, uploadWebConfig: config.uploadWebConfig, submitKey: config.submitKey } });
-
-  postConfig.lastVersion = versionId;
-  postConfig.lastUpdateInfo = updateInfo;
-  postConfig.lastTodaySubVersion++;
-
-  const updateSource = (await select({
-    choices: [
-      {
-        name: '重新构建',
-        value: 'rebuild',
-      },
-      {
-        name: '已上传的阿里OSS文件路径',
-        value: 'uploaded-alioss',
-      },
-    ],
-    message: '选择上传来源',
-    default: 'rebuild',
-  }));
-
-  const force = await confirm({ message: '强制更新?', default: false });
-  const updateNext = hotfix ? false : await confirm({ message: '作为下个版本?', default: false });
-  const versionCode = config.buildAppGetVersion();
-
-  await saveConfig(postConfig);
-
-  if (updateSource === 'rebuild') 
-    await config.buildAppCallback(param, versionCode, postConfig.lastTodaySubVersion);
-  else if (updateSource === 'uploaded-alioss') {
-    const fileName = await input({ message: '输入已上传的阿里OSS文件路径' });
-    try {
-      const result = (await axiosInstance.post('/update-post', {
-        type: 2,
-        ossConfig: {
-          ossPath: fileName,
-          ossPublic: '',
-        },
-        uploadAppConfig: {
-          updateAsNext: updateNext,
-        },
-        versionId,
-        updateInfo,
-        versionCode: versionCode
-      })).data;
-      console.log('上传成功');
-      console.log('新更新ID: ' + result.updateId);
-    } catch (e) {
-      console.error('上传失败', e);
-    }
-    return;
-  }
-  else {
-    console.error('错误的选择');
-    return;
-  }
-
-  const uploadFile = await config.buildAppGetUploadFile(param);
-  try {
-    await access(uploadFile, constants.R_OK)
-  } catch {
-    console.error(`Failed to access ${uploadFile}, did you created it?`);
-    return;
-  }
-
-  console.log('开始上传');
-
-  //小于8mb则小文件上传,否则使用阿里OSS上传
-  const fileInfo = await stat(uploadFile);
-  if (fileInfo.size < 8 * 1024 * 1024) {
-    const appData = (await readFile(uploadFile));
-
-    const formData = new FormData();
-    formData.append("file", new Blob([ appData ], { type : 'application/zip' }));
-    formData.append("type", 2);
-    formData.append("versionId", versionId);
-    formData.append("updateInfo", updateInfo);
-    formData.append("uploadAppConfig", { updateAsNext: updateNext });
-    formData.append("force", force);
-    formData.append("versionCode", versionCode);
-
-    try {
-      const result = (await axiosInstance.post('/update-post', formData, {
-        headers: { 'Content-Type': 'multipart/form-data' }
-      })).data;
-      console.log('上传成功');
-      console.log('新更新ID: ' + result.updateId);
-      console.log('删除构建文件');
-
-      await unlink(uploadFile);
-    } catch (e) {
-      console.error('上传失败', e);
-    }
-
-  } else {
-    //阿里OSS上传
-    //请求STS进行临时授权
-    const stsToken = (await axiosInstance.post('/update-ali-oss-sts')).data;
-    const client = new OSS({
-      region: stsToken.Region,
-      accessKeyId: stsToken.AccessKeyId,
-      accessKeySecret: stsToken.AccessKeySecret,
-      stsToken: stsToken.SecurityToken,
-      bucket: stsToken.Bucket,
-      refreshSTSToken: async () => {
-        const refreshToken = (await axiosInstance.get("/update-ali-oss-sts")).data;
-        return {
-          accessKeyId: refreshToken.AccessKeyId,
-          accessKeySecret: refreshToken.AccessKeySecret,
-          stsToken: refreshToken.SecurityToken,
-        };
-      },
-    });
-    
-    //小于96mb则直接上传,否则分片上传
-    const fileName = `/${await config.buildAppGetOSSFileName(param)}`;
-
-    console.log('Start upload to ali oss');
-
-    let returnUrl = '';
-    if (fileInfo.size < 96 * 1024 * 1024) {
-      const result = await client.put(fileName, uploadFile);
-      returnUrl = result.url;
-    } else {
-      const bar1 = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);
-      bar1.start(100, 0);
-      await aliOSSMultipartUpload(client, fileName, uploadFile, (p) => {
-        bar1.update(p * 100);
-      });
-      bar1.update(100);
-      bar1.stop();
-    }
-
-    console.log('Upload to ali oss done');
-  
-    try {
-      const result = (await axiosInstance.post('/update-post', {
-        type: 2,
-        ossConfig: {
-          ossPath: fileName,
-          ossPublic: returnUrl,
-        },
-        uploadAppConfig: {
-          updateAsNext: updateNext,
-        },
-        versionId,
-        updateInfo,
-        versionCode: versionCode
-      })).data;
-      console.log('上传成功');
-      console.log('新更新ID: ' + result.updateId);
-    } catch (e) {
-      console.error('上传失败', e);
-    }
-  }
-}
-
-//Web更新与提交
-//========================================
-
-export async function postWebUpdate(axiosInstance, param) {
-  const skipBuild = param.skip
-  const noDelete = param.ndelete;
-  
-  const postConfig = await getConfig();
-  const versionId = await selectVersion(false, postConfig.lastVersion);
-  const updateInfo = await getUpdateInfo(postConfig);
-
-  postConfig.lastVersion = versionId;
-  postConfig.lastUpdateInfo = updateInfo;
-  postConfig.lastTodaySubVersion++;
-
-  await axiosInstance.post('/update-post', { config: { type: 1, test: true, versionId, uploadWebConfig: config.uploadWebConfig, submitKey: config.submitKey } });
-
-  const now = new Date();
-  const versionCode = await config.buildWebVersionGenerateCommand(now, postConfig.lastTodaySubVersion);
-
-  if (config.buildWebOutVersionPath)
-    await writeFile(path.resolve(__dirname, config.buildWebOutVersionPath), versionCode);
-
-  await saveConfig(postConfig);
-  
-  if (!skipBuild && config.buildWebCommand) {
-    console.log('正在执行构建...');
-
-    await new Promise((resolve, reject) => {
-      exec(config.buildWebCommand, function(err, stdout) {
-        if (err)
-          reject(err);
-        else 
-          resolve();
-      });
-    });
-
-    console.log('构建完成');
-  }
-
-  const distDir = path.resolve(__dirname, config.buildWebOutDir);
-
-  try {
-    await access(distDir, constants.R_OK)
-  } catch {
-    console.error(`Failed to access ${distDir}`);
-    return;
-  }
-
-  const outputPath = __dirname + '/upload.zip';
-  const skipFiles = config?.buildWebOptions?.skipFiles ?? [];
-
-  function checkPathSkip(path) {
-    if (!skipFiles || skipFiles.length === 0)
-      return;
-    return skipFiles.find((item) => path.startsWith(item));
-  }
-
-  if (!skipBuild) {
-    console.log('开始压缩zip...');
-
-    const output = fs.createWriteStream(outputPath);
-    const archive = archiver('zip', {
-      zlib: { level: 9 } // Sets the compression level.
-    });
-    archive.pipe(output);
-
-    async function loopDir(path, subPrefix) {
-      const files = await readdir(path);
-      for (const file of files) {
-        const subPath = subPrefix + '/' + file;
-        if (checkPathSkip(subPath))
-          continue;
-        const filestat = await stat(distDir + subPath);
-        if (filestat.isDirectory()) {
-          await loopDir(distDir + subPath, subPath);
-        } else {
-          archive.file(distDir + subPath, { name: subPath });
-        }
-      }
-    }
-    await loopDir(distDir, '')
-
-    console.log('等待压缩zip...');
-
-    const waitArchive = new Promise((resolve, reject) => {
-      output.on('close', function() {
-        console.log(archive.pointer() + ' total bytes');
-        console.log('archiver has been finalized and the output file descriptor has closed.');
-        resolve();
-      });
-      archive.on('error', function(err) {
-        reject(err);
-      });
-    })
-
-    await archive.finalize();
-    await waitArchive;
-  }
-
-  console.log('开始上传zip');
-
-  let success = false;
-
-  //小于8mb则小文件上传,否则分片上传
-  const fileInfo = await stat(outputPath);
-  const formData = new FormData();
-  const submitConfig = {
-    type: 1,
-    versionId,
-    updateInfo,
-    versionCode,
-    uploadWebConfig: config.uploadWebConfig,
-  }
-
-  if (fileInfo.size < 8 * 1024 * 1024) {
-    const uploadZipContent = await readFile(outputPath);
-    formData.append("file", new Blob([ uploadZipContent ], { type : 'application/zip' }), 'upload.zip');
-  } else {
-    const multuploadInfo = (await axiosInstance.post('/update-large-token', {
-      fileSize: fileInfo.size,
-      fileName: path.basename(outputPath)
-    })).data;
-    const chunkSize = multuploadInfo.splitPartSize;
-
-    const bar1 = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);
-    bar1.start(100, 0);
-
-    for (let i = 0; i < multuploadInfo.allChunks; i++) {
-      const start = i * chunkSize;
-      const len = Math.min(start + chunkSize, fileInfo.size) - start;
-      const uploadZipContent = await readFileRange(outputPath, start, len);
-      const subFormData = new FormData();
-      subFormData.append("file", new Blob([ uploadZipContent ], { type : 'application/zip' }), 'upload.zip');
-      subFormData.append("key", multuploadInfo.key);
-      subFormData.append("info", JSON.stringify({}));
-      
-      (await axiosInstance.post('/update-large', subFormData, {
-        headers: { 'Content-Type': 'multipart/form-data' }
-      })).data;
-      
-      bar1.update(Math.floor(i / multuploadInfo.allChunks * 100));
-    }
-
-    bar1.update(100);
-    bar1.stop();
-
-    submitConfig.multuploadedKey = multuploadInfo.key;
-  }
-
-  try {
-    formData.append("config", JSON.stringify(submitConfig));
-    const result = (await axiosInstance.post('/update-post', formData, {
-      headers: { 'Content-Type': 'multipart/form-data' }
-    })).data;
-    console.log('上传成功');
-    console.log('新更新ID: ' + result.updateId);
-    success = true;
-  } catch (e) {
-    console.error('上传失败', e);
-  }
-  if (!success || noDelete) 
-    return;
-    
-  console.log('删除zip');
-  await unlink(outputPath);
-}

+ 0 - 74
src/stores/auth.ts

@@ -1,74 +0,0 @@
-import UserApi, { LoginResult, UserInfo } from "@/api/auth/UserApi";
-import { defineStore } from "pinia"
-
-const STORAGE_KEY = 'authInfo';
-
-export const useAuthStore = defineStore('auth', {
-  state: () => ({
-    token: '',
-    expireAt: 0,
-    userId: 0,
-    userInfo: null as null|UserInfo,
-  }),
-  actions: {
-    async loadLoginState() {
-      try {
-        const res = localStorage.getItem(STORAGE_KEY);
-        if (!res)
-          throw 'no storage';
-        const authInfo = JSON.parse(res);
-        this.token = authInfo.token;
-        this.userId = authInfo.userId;
-        this.expireAt = authInfo.expireAt;
-
-        // 检查token是否过期,如果快要过期,则刷新token
-        if (Date.now() > this.expireAt + 1000 * 3600 * 5) {
-          const refreshResult = await UserApi.refresh();
-          this.loginResultHandle(refreshResult);
-          this.userInfo = refreshResult.userInfo;
-        } else {
-          this.userInfo = await UserApi.getUserInfo(this.userId);
-        }
-      } catch (error) {
-        this.token = '';
-        this.userId = 0;
-        this.userInfo = null;
-
-        console.log('loadLoginState', error);
-      }
-    },
-    async loginAdmin(account: string, password: string) {
-      const loginResult = await UserApi.loginAdmin({
-        account,
-        password,
-      })
-      this.loginResultHandle(loginResult);
-    },
-    async loginResultHandle(loginResult: LoginResult) {
-      this.token = loginResult.userInfo.token;
-      this.userId = loginResult.userInfo.id;
-      this.userInfo = loginResult.userInfo;
-      this.expireAt = loginResult.userInfo.expiresIn + Date.now();
-
-      localStorage.setItem(STORAGE_KEY, 
-        JSON.stringify({ 
-          token: this.token, 
-          userId: this.userId ,
-          expireAt: this.expireAt,
-        }) 
-      );
-    },
-    async logout() {
-      this.token = '';
-      this.userId = 0;
-      this.userInfo = null;
-
-      localStorage.removeItem(STORAGE_KEY);
-    }
-  },
-  getters: {
-    isLogged(state) {
-      return state.token != '' && state.userId != 0
-    },
-  },
-})

+ 0 - 1
test

@@ -1 +0,0 @@
-Subproject commit 2d48ba9ace084d31ed9aae89ccaa2dac8521ea7b

+ 0 - 4
tsconfig.json

@@ -1,4 +0,0 @@
-{
-  // https://nuxt.com/docs/guide/concepts/typescript
-  "extends": "./.nuxt/tsconfig.json"
-}