commit 13b28ceec9d740710e0cf2f58d8e515a23d48683 Author: zdz Date: Thu Mar 19 15:02:23 2026 +0800 initiate diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..c989e60 --- /dev/null +++ b/.env.production @@ -0,0 +1,3 @@ +# 生产环境 +VITE_MODE = 'production' +VITE_BASE_URL = '//ser.szzztec.com/stock' \ No newline at end of file diff --git a/.env.test b/.env.test new file mode 100644 index 0000000..dad076d --- /dev/null +++ b/.env.test @@ -0,0 +1,3 @@ +# 测试环境 +VITE_MODE = 'test' +VITE_BASE_URL = '//testser.szzztec.com/stock' \ No newline at end of file diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..7c4376e --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,18 @@ +/* eslint-env node */ +require('@rushstack/eslint-patch/modern-module-resolution') + +module.exports = { + root: true, + extends: [ + 'plugin:vue/vue3-essential', + 'eslint:recommended', + '@vue/eslint-config-typescript', + '@vue/eslint-config-prettier/skip-formatting' + ], + rules: { + 'vue/multi-word-component-names': 'off' + }, + parserOptions: { + ecmaVersion: 'latest' + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..93c76e6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.DS_Store +dist +dist-ssr +coverage +*.local +/cypress/videos/ +/cypress/screenshots/ +.env.development +auto-imports.d.ts +components.d.ts +package-lock.json +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*.tsbuildinfo diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..66e2335 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/prettierrc", + "semi": false, + "tabWidth": 2, + "singleQuote": true, + "printWidth": 100, + "trailingComma": "none" +} \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..93ea3e7 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "Vue.volar", + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode" + ] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..2344d40 --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# stock-h5 + +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://vitejs.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 +``` + +### Run Unit Tests with [Vitest](https://vitest.dev/) + +```sh +npm run test:unit +``` + +### Lint with [ESLint](https://eslint.org/) + +```sh +npm run lint +``` diff --git a/env.d.ts b/env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/index.html b/index.html new file mode 100644 index 0000000..866f090 --- /dev/null +++ b/index.html @@ -0,0 +1,217 @@ + + + + + + + + + + 数据管理系统 + + <% for (const i of cdnCss) { %> + + <% } %> + + <% for (const i of cdnJs) { %> + + <% } %> + + + +
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
第一上海证券有限公司
+
FIRST SHANGHAI SECURITIES LIMITED
+
香港中環德輔道中 71 號永安集團大廈 19 樓D
+
19/F, Wing On House, 71 Des Voeux Road Central, Hong Kong
+
电话 Tel:(852)2532 1580 传真 Fax:(852)2537 6911
+
+
+
+ + + + \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..5633a2a --- /dev/null +++ b/package.json @@ -0,0 +1,68 @@ +{ + "name": "stock-h5", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --mode development", + "build": "run-p type-check \"build-only {@}\" --", + "build:test": "vite build --mode test", + "build:prod": "vite build --mode production", + "preview": "vite preview", + "test:unit": "vitest", + "build-only": "vite build", + "type-check": "vue-tsc --build --force", + "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", + "format": "prettier --write src/" + }, + "dependencies": { + "@vant/auto-import-resolver": "^1.2.1", + "@vant/touch-emulator": "^1.4.0", + "axios": "^1.7.2", + "crypto-js": "^4.2.0", + "echarts": "^5.5.1", + "markdown-it": "^14.1.0", + "mitt": "^3.0.1", + "moment": "^2.30.1", + "pinia": "^2.1.7", + "pinia-plugin-persistedstate": "^3.2.1", + "rollup-plugin-external-globals": "^0.10.0", + "rollup-plugin-visualizer": "^5.12.0", + "sass": "^1.77.8", + "sass-loader": "^16.0.0", + "scss": "^0.2.4", + "secure-ls": "^2.0.0", + "unplugin-auto-import": "^0.17.6", + "unplugin-vue-components": "^0.27.0", + "vant": "^4.9.1", + "vite-plugin-html": "^3.2.2", + "vue": "^3.4.21", + "vue-router": "^4.3.0", + "vue-virtual-scroller": "^2.0.0-beta.8", + "vue-wechat-title": "^2.0.7", + "weixin-js-sdk": "^1.6.5" + }, + "devDependencies": { + "@rushstack/eslint-patch": "^1.8.0", + "@tsconfig/node20": "^20.1.4", + "@types/jsdom": "^21.1.6", + "@types/node": "^20.12.5", + "@vitejs/plugin-vue": "^5.0.4", + "@vue/eslint-config-prettier": "^9.0.0", + "@vue/eslint-config-typescript": "^13.0.0", + "@vue/test-utils": "^2.4.5", + "@vue/tsconfig": "^0.5.1", + "eslint": "^8.57.0", + "eslint-plugin-vue": "^9.23.0", + "jsdom": "^24.0.0", + "npm-run-all2": "^6.1.2", + "prettier": "^3.2.5", + "terser": "^5.31.1", + "typescript": "~5.4.0", + "vite": "^5.2.8", + "vite-plugin-cdn-import": "^1.0.1", + "vite-plugin-compression": "^0.5.1", + "vitest": "^1.4.0", + "vue-tsc": "^2.0.11" + } +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..d4a62f1 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/img/CLBG.jpeg b/public/img/CLBG.jpeg new file mode 100644 index 0000000..b54cd4e Binary files /dev/null and b/public/img/CLBG.jpeg differ diff --git a/public/img/GXBG.jpeg b/public/img/GXBG.jpeg new file mode 100644 index 0000000..0cde0c3 Binary files /dev/null and b/public/img/GXBG.jpeg differ diff --git a/public/img/GXBG.jpg b/public/img/GXBG.jpg new file mode 100644 index 0000000..1a0525b Binary files /dev/null and b/public/img/GXBG.jpg differ diff --git a/public/img/SFBG.jpeg b/public/img/SFBG.jpeg new file mode 100644 index 0000000..b8ba1c5 Binary files /dev/null and b/public/img/SFBG.jpeg differ diff --git a/public/img/XLL.jpeg b/public/img/XLL.jpeg new file mode 100644 index 0000000..eed535b Binary files /dev/null and b/public/img/XLL.jpeg differ diff --git a/public/img/XLL.jpg b/public/img/XLL.jpg new file mode 100644 index 0000000..12f321f Binary files /dev/null and b/public/img/XLL.jpg differ diff --git a/public/img/commentBg.jpg b/public/img/commentBg.jpg new file mode 100644 index 0000000..fa1c895 Binary files /dev/null and b/public/img/commentBg.jpg differ diff --git a/public/img/completeLogo.jpg b/public/img/completeLogo.jpg new file mode 100644 index 0000000..789c7d7 Binary files /dev/null and b/public/img/completeLogo.jpg differ diff --git a/public/img/headPic.jpg b/public/img/headPic.jpg new file mode 100644 index 0000000..2edee7d Binary files /dev/null and b/public/img/headPic.jpg differ diff --git a/public/img/home-banner.jpg b/public/img/home-banner.jpg new file mode 100644 index 0000000..a564303 Binary files /dev/null and b/public/img/home-banner.jpg differ diff --git a/public/img/indicate.jpeg b/public/img/indicate.jpeg new file mode 100644 index 0000000..a912b35 Binary files /dev/null and b/public/img/indicate.jpeg differ diff --git a/public/img/indicate.png b/public/img/indicate.png new file mode 100644 index 0000000..a3b097c Binary files /dev/null and b/public/img/indicate.png differ diff --git a/public/img/loginLogo.jpg b/public/img/loginLogo.jpg new file mode 100644 index 0000000..f0e93fe Binary files /dev/null and b/public/img/loginLogo.jpg differ diff --git a/public/img/night.png b/public/img/night.png new file mode 100644 index 0000000..2ef895e Binary files /dev/null and b/public/img/night.png differ diff --git a/public/img/smallLogo.jpg b/public/img/smallLogo.jpg new file mode 100644 index 0000000..2edee7d Binary files /dev/null and b/public/img/smallLogo.jpg differ diff --git a/public/img/user-bg.jpeg b/public/img/user-bg.jpeg new file mode 100644 index 0000000..c4ffa40 Binary files /dev/null and b/public/img/user-bg.jpeg differ diff --git a/public/img/user.png b/public/img/user.png new file mode 100644 index 0000000..582ec94 Binary files /dev/null and b/public/img/user.png differ diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..84d10aa --- /dev/null +++ b/src/App.vue @@ -0,0 +1,276 @@ + + + + + \ No newline at end of file diff --git a/src/assets/base.css b/src/assets/base.css new file mode 100644 index 0000000..5ab9c63 --- /dev/null +++ b/src/assets/base.css @@ -0,0 +1,542 @@ +/* color palette from */ +:root { + --vt-c-white: #ffffff; + --vt-c-white-soft: #f8f8f8; + --vt-c-white-mute: #f2f2f2; + + --vt-c-black: #181818; + --vt-c-black-soft: #222222; + --vt-c-black-mute: #282828; + + --vt-c-indigo: #2c3e50; + + --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); + --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); + --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); + --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); + + --vt-c-text-light-1: var(--vt-c-indigo); + --vt-c-text-light-2: rgba(60, 60, 60, 0.66); + --vt-c-text-dark-1: var(--vt-c-white); + --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); + --van-gray-3: #dcdee0; +} + +/* semantic color variables for this project */ +:root { + --color-background: var(--vt-c-white); + --color-background-soft: var(--vt-c-white-soft); + --color-background-mute: var(--vt-c-white-mute); + + --color-border: var(--vt-c-divider-light-2); + --color-border-hover: var(--vt-c-divider-light-1); + + --color-heading: var(--vt-c-text-light-1); + --color-text: var(--vt-c-text-light-1); + --safe-area-inset-bottom: env(safe-area-inset-bottom); + + --section-gap: 160px; +} + +@media (prefers-color-scheme: dark) { + :root { + --color-background: var(--vt-c-black); + --color-background-soft: var(--vt-c-black-soft); + --color-background-mute: var(--vt-c-black-mute); + + --color-border: var(--vt-c-divider-dark-2); + --color-border-hover: var(--vt-c-divider-dark-1); + + --color-heading: var(--vt-c-text-dark-1); + --color-text: var(--vt-c-text-dark-2); + } +} + +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + font-weight: normal; +} +body { + min-height: 100vh; + color: var(--color-text); + background: var(--color-background); + transition: + color 0.5s, + background-color 0.5s; + font-family: + Inter, + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + Roboto, + Oxygen, + Ubuntu, + Cantarell, + 'Fira Sans', + 'Droid Sans', + 'Helvetica Neue', + sans-serif; + font-size: 15px; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* vant */ +.van-popup--bottom { + @media screen and (min-width: 992px) { + left: calc(50vw - 496px) !important; + right: calc(50vw - 496px) !important; + width: 992px !important; + } +} + +.box-popup { + min-height: 372px; + max-height: 500px; + overflow-y: auto; +} + +.page-bg { + .van-tag { + border-radius: 4px; + } +} + +.tabbar-list { + padding-bottom: 70px; + overflow-y: auto; + height: 100vh; +} + +.page-bg { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + background-color: #ededed; + bottom: env(safe-area-inset-bottom); +} + +.page-content { + background-color: #F2F3F5; + max-width: 992px; + margin: 0 auto; + height: 100vh; + padding-bottom: env(safe-area-inset-bottom); +} + +.page-div { + background-color: #F2F3F5; + max-width: 992px; + margin: 0 auto; + height: 100vh; + padding-bottom: env(safe-area-inset-bottom); + display: flex; + flex-direction: column; +} +/* .van-cell { + margin: 0 16px 12px; + width: calc(100% - 32px); + box-shadow: 0 2px 8px 0 rgba(0, 0, 0, .1); + + @media screen and (min-width: 678px) { + width: 642px; + } + + &:last-child { + margin-bottom: 0; + } +} */ + +.card { + padding: 10px; + border-radius: 6px; + /* width: calc(100% - 32px); */ + box-shadow: 0 2px 8px 0 rgba(0, 0, 0, .1); +} +.col-6 { + width: 50%; +} +.col-12 { + width: 100%; +} +.col-4 { + width: 33.3%; +} +.van-tabbar { + height: 60px !important; + .van-icon { + font-size: 26px !important; + } + .van-tabbar-item__text { + font-size: 16px !important; + } +} + +.van-cell { + border-radius: 8px; +} + +.top-select-list { + padding-top: 40px; +} +.text-ellipsis { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + word-break: break-all; +} + +.top-select { + position: fixed; + top: 0; + right: 0; + left: 0; + height: 40px; + z-index: 4; + background-color: #fff; + + @media screen and (min-width: 678px) { + left: calc(50vw - 339px); + right: calc(50vw - 339px); + } + + .van-grid-item__content { + padding: 0; + height: 38px; + line-height: 38px; + + .grid-item-text { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + text-align: center; + max-width: calc(33vw - 28px); + font-size: 12px; + + @media screen and (min-width: 678px) { + max-width: 200px; + } + } + } +} +.verticalSwiper { + background-color: #fff; + width: 100vh !important; + height: 100vh; + transform-origin: 50vw 50vw; + transform: rotate(90deg) !important; + overflow: hidden; + .top-select { + display: none; + } + .chart { + width: 100vh; + height: 100vw; + } + + .chartDiv { + width: 100%; + height: 100%; + } +} + +.horizontalSwiper { + background-color: #fff; + .top-select { + width: 100px; + } + .chart { + width: 100vw; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + } + + .chartDiv { + width: 100%; + height: 40vh; + } +} + +.chart { + position: relative; + .icon { + position: absolute; + left: 50%; + transform: translateX(-50%); + background-color: #f1f1f1; + padding: 10px; + border-radius: 100%; + z-index: 999; + } + + .icon-up { + top: 20px; + } + + .icon-down { + bottom: 20px; + } + .loading { + position: absolute; + z-index: 2; + } +} +.custom-style { + padding: 10px; + +} +ol { + padding-left: 10px !important; + li { + list-style-type: decimal; + list-style-position: inside; + } +} + +/* iconfont */ +.iconfont { + font-size: 14px; +} + +/* font-size */ +.content { + font-size: 14px; +} + +.font-12 { + font-size: 12px; +} +strong { + font-weight: bold; +} + +/* color */ +.primary-text-color { + color: #1989fa !important; +} + +.disabled-primary-background-color { + background-color: #90d7ec !important; +} + +.primary-background-color { + background-color: #1989fa !important; +} + +.warning-text-color { + color: #ff976a !important; +} + +.warning-background-color { + background-color: #ff976a !important; +} + +.success-text-color { + color: #07c160 !important; +} + +.success-background-color { + background-color: #07c160 !important; +} + +.danger-text-color { + color: #f56c6c !important; +} + +.danger-background-color { + background-color: #f56c6c !important; +} + +.balck-text-color { + color: #130c0e !important; +} + +.yellow-background-color { + background-color: #e6a23c !important; +} + +.pink-background-color { + background-color: #f173ac !important; +} + +.purple-background-color { + background-color: #8552a1 !important; +} + +.other-background-color { + background-color: #8390e5 !important; +} + +.active { + background-color: #1989fa; + color: #fff; +} + +.activeColor { + color: #1989fa; +} + +.white-text-color { + color: #fff; +} + +.disbaled-text-color { + color: #c8c9cc; +} + +.disbaled-background-color { + background-color: #c8c9cc; +} + +/* flex */ + +.flex-wrap { + flex-wrap: wrap; +} + +.justify-content-between { + justify-content: space-between; +} + +.justify-content-center { + justify-content: center; +} + +.justify-content-end { + justify-content: end; +} + +.align-items-start { + align-items: flex-start; +} + +.align-items-center { + align-items: center; +} + +.align-items-end { + align-items: flex-end; +} + +/* padding */ + +.p-10 { + padding: 10px; +} + +.p-8 { + padding: 8px; +} + +.ps-16 { + padding-left: 16px; +} + +.pe-16 { + padding-right: 16px; +} + +.pt-5 { + padding-top: 5; +} + +.pb-5 { + padding-bottom: 5px; +} + +/* margin */ +.m-5 { + margin: 5px; +} + +.m-16 { + margin: 16px; +} + +.mt-5 { + margin-top: 5px; +} + +.ms-10 { + margin-left: 10px; +} + +.mt-10 { + margin-top: 10px; +} + +.mb-10 { + margin-bottom: 10px; +} +.mx-10 { + margin: 0 10px; +} +/* border */ +.border-1 { + border: 1px solid #dcdee0; +} + +.border-0 { + border: 0 !important; +} + +.border-bottom-1 { + border-bottom: 1px solid #dcdee0; +} + +.border-right-1 { + border-right: 1px solid #dcdee0; +} + +.border-radius-4 { + border-radius: 4px; +} + +.border-radius-8 { + border-radius: 8px; +} + +/* 省略号 */ +.ellipsis-one { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + word-break: break-all; +} + +.addDiv { + position: fixed; + bottom: calc(env(safe-area-inset-bottom) + 70px); + right: 20px; + height: 60px; + width: 60px; + border-radius: 100%; + color: #fff; + z-index: 2; + + @media screen and (min-width: 678px) { + right: calc(50vw - 319px); + } + + .text-center { + height: 30px; + width: 60px; + line-height: 16px; + } + + .addDiv1 { + padding-top: 14px; + } + + .addDiv2 { + padding-bottom: 14px; + } +} diff --git a/src/assets/iconfont/demo.css b/src/assets/iconfont/demo.css new file mode 100644 index 0000000..a67054a --- /dev/null +++ b/src/assets/iconfont/demo.css @@ -0,0 +1,539 @@ +/* Logo 字体 */ +@font-face { + font-family: "iconfont logo"; + src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834'); + src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg'); +} + +.logo { + font-family: "iconfont logo"; + font-size: 160px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* tabs */ +.nav-tabs { + position: relative; +} + +.nav-tabs .nav-more { + position: absolute; + right: 0; + bottom: 0; + height: 42px; + line-height: 42px; + color: #666; +} + +#tabs { + border-bottom: 1px solid #eee; +} + +#tabs li { + cursor: pointer; + width: 100px; + height: 40px; + line-height: 40px; + text-align: center; + font-size: 16px; + border-bottom: 2px solid transparent; + position: relative; + z-index: 1; + margin-bottom: -1px; + color: #666; +} + + +#tabs .active { + border-bottom-color: #f00; + color: #222; +} + +.tab-container .content { + display: none; +} + +/* 页面布局 */ +.main { + padding: 30px 100px; + width: 960px; + margin: 0 auto; +} + +.main .logo { + color: #333; + text-align: left; + margin-bottom: 30px; + line-height: 1; + height: 110px; + margin-top: -50px; + overflow: hidden; + *zoom: 1; +} + +.main .logo a { + font-size: 160px; + color: #333; +} + +.helps { + margin-top: 40px; +} + +.helps pre { + padding: 20px; + margin: 10px 0; + border: solid 1px #e7e1cd; + background-color: #fffdef; + overflow: auto; +} + +.icon_lists { + width: 100% !important; + overflow: hidden; + *zoom: 1; +} + +.icon_lists li { + width: 100px; + margin-bottom: 10px; + margin-right: 20px; + text-align: center; + list-style: none !important; + cursor: default; +} + +.icon_lists li .code-name { + line-height: 1.2; +} + +.icon_lists .icon { + display: block; + height: 100px; + line-height: 100px; + font-size: 42px; + margin: 10px auto; + color: #333; + -webkit-transition: font-size 0.25s linear, width 0.25s linear; + -moz-transition: font-size 0.25s linear, width 0.25s linear; + transition: font-size 0.25s linear, width 0.25s linear; +} + +.icon_lists .icon:hover { + font-size: 100px; +} + +.icon_lists .svg-icon { + /* 通过设置 font-size 来改变图标大小 */ + width: 1em; + /* 图标和文字相邻时,垂直对齐 */ + vertical-align: -0.15em; + /* 通过设置 color 来改变 SVG 的颜色/fill */ + fill: currentColor; + /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示 + normalize.css 中也包含这行 */ + overflow: hidden; +} + +.icon_lists li .name, +.icon_lists li .code-name { + color: #666; +} + +/* markdown 样式 */ +.markdown { + color: #666; + font-size: 14px; + line-height: 1.8; +} + +.highlight { + line-height: 1.5; +} + +.markdown img { + vertical-align: middle; + max-width: 100%; +} + +.markdown h1 { + color: #404040; + font-weight: 500; + line-height: 40px; + margin-bottom: 24px; +} + +.markdown h2, +.markdown h3, +.markdown h4, +.markdown h5, +.markdown h6 { + color: #404040; + margin: 1.6em 0 0.6em 0; + font-weight: 500; + clear: both; +} + +.markdown h1 { + font-size: 28px; +} + +.markdown h2 { + font-size: 22px; +} + +.markdown h3 { + font-size: 16px; +} + +.markdown h4 { + font-size: 14px; +} + +.markdown h5 { + font-size: 12px; +} + +.markdown h6 { + font-size: 12px; +} + +.markdown hr { + height: 1px; + border: 0; + background: #e9e9e9; + margin: 16px 0; + clear: both; +} + +.markdown p { + margin: 1em 0; +} + +.markdown>p, +.markdown>blockquote, +.markdown>.highlight, +.markdown>ol, +.markdown>ul { + width: 80%; +} + +.markdown ul>li { + list-style: circle; +} + +.markdown>ul li, +.markdown blockquote ul>li { + margin-left: 20px; + padding-left: 4px; +} + +.markdown>ul li p, +.markdown>ol li p { + margin: 0.6em 0; +} + +.markdown ol>li { + list-style: decimal; +} + +.markdown>ol li, +.markdown blockquote ol>li { + margin-left: 20px; + padding-left: 4px; +} + +.markdown code { + margin: 0 3px; + padding: 0 5px; + background: #eee; + border-radius: 3px; +} + +.markdown strong, +.markdown b { + font-weight: 600; +} + +.markdown>table { + border-collapse: collapse; + border-spacing: 0px; + empty-cells: show; + border: 1px solid #e9e9e9; + width: 95%; + margin-bottom: 24px; +} + +.markdown>table th { + white-space: nowrap; + color: #333; + font-weight: 600; +} + +.markdown>table th, +.markdown>table td { + border: 1px solid #e9e9e9; + padding: 8px 16px; + text-align: left; +} + +.markdown>table th { + background: #F7F7F7; +} + +.markdown blockquote { + font-size: 90%; + color: #999; + border-left: 4px solid #e9e9e9; + padding-left: 0.8em; + margin: 1em 0; +} + +.markdown blockquote p { + margin: 0; +} + +.markdown .anchor { + opacity: 0; + transition: opacity 0.3s ease; + margin-left: 8px; +} + +.markdown .waiting { + color: #ccc; +} + +.markdown h1:hover .anchor, +.markdown h2:hover .anchor, +.markdown h3:hover .anchor, +.markdown h4:hover .anchor, +.markdown h5:hover .anchor, +.markdown h6:hover .anchor { + opacity: 1; + display: inline-block; +} + +.markdown>br, +.markdown>p>br { + clear: both; +} + + +.hljs { + display: block; + background: white; + padding: 0.5em; + color: #333333; + overflow-x: auto; +} + +.hljs-comment, +.hljs-meta { + color: #969896; +} + +.hljs-string, +.hljs-variable, +.hljs-template-variable, +.hljs-strong, +.hljs-emphasis, +.hljs-quote { + color: #df5000; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-type { + color: #a71d5d; +} + +.hljs-literal, +.hljs-symbol, +.hljs-bullet, +.hljs-attribute { + color: #0086b3; +} + +.hljs-section, +.hljs-name { + color: #63a35c; +} + +.hljs-tag { + color: #333333; +} + +.hljs-title, +.hljs-attr, +.hljs-selector-id, +.hljs-selector-class, +.hljs-selector-attr, +.hljs-selector-pseudo { + color: #795da3; +} + +.hljs-addition { + color: #55a532; + background-color: #eaffea; +} + +.hljs-deletion { + color: #bd2c00; + background-color: #ffecec; +} + +.hljs-link { + text-decoration: underline; +} + +/* 代码高亮 */ +/* PrismJS 1.15.0 +https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ +code[class*="language-"], +pre[class*="language-"] { + color: black; + background: none; + text-shadow: 0 1px white; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, +pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, +code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; +} + +pre[class*="language-"]::selection, +pre[class*="language-"] ::selection, +code[class*="language-"]::selection, +code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; +} + +@media print { + + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; +} + +:not(pre)>code[class*="language-"], +pre[class*="language-"] { + background: #f5f2f0; +} + +/* Inline code */ +:not(pre)>code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.namespace { + opacity: .7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #9a6e3a; + background: hsla(0, 0%, 100%, .5); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function, +.token.class-name { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/src/assets/iconfont/demo_index.html b/src/assets/iconfont/demo_index.html new file mode 100644 index 0000000..0ed4006 --- /dev/null +++ b/src/assets/iconfont/demo_index.html @@ -0,0 +1,487 @@ + + + + + iconfont Demo + + + + + + + + + + + + + +
+

+ + +

+ +
+
+
    + +
  • + +
    链接
    +
    &#xe600;
    +
  • + +
  • + +
    良品铺子
    +
    &#xe61f;
    +
  • + +
  • + +
    向下双箭头
    +
    &#xe62d;
    +
  • + +
  • + +
    月历
    +
    &#xe6c4;
    +
  • + +
  • + +
    KHCFDC_音频文件
    +
    &#xe6ed;
    +
  • + +
  • + +
    excel
    +
    &#xe8a6;
    +
  • + +
  • + +
    image
    +
    &#xe8aa;
    +
  • + +
  • + +
    pdf
    +
    &#xe8af;
    +
  • + +
  • + +
    ppt
    +
    &#xe8b0;
    +
  • + +
  • + +
    txt
    +
    &#xe8b4;
    +
  • + +
  • + +
    unknown
    +
    &#xe8b5;
    +
  • + +
  • + +
    word
    +
    &#xe8b6;
    +
  • + +
  • + +
    默认文件夹
    +
    &#xe6ea;
    +
  • + +
+
+

Unicode 引用

+
+ +

Unicode 是字体在网页端最原始的应用方式,特点是:

+
    +
  • 支持按字体的方式去动态调整图标大小,颜色等等。
  • +
  • 默认情况下不支持多色,直接添加多色图标会自动去色。
  • +
+
+

注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)

+
+

Unicode 使用步骤如下:

+

第一步:拷贝项目下面生成的 @font-face

+
@font-face {
+  font-family: 'iconfont';
+  src: url('iconfont.woff2?t=1755072825017') format('woff2'),
+       url('iconfont.woff?t=1755072825017') format('woff'),
+       url('iconfont.ttf?t=1755072825017') format('truetype');
+}
+
+

第二步:定义使用 iconfont 的样式

+
.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+

第三步:挑选相应图标并获取字体编码,应用于页面

+
+<span class="iconfont">&#x33;</span>
+
+
+

"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

+
+
+
+
+
    + +
  • + +
    + 链接 +
    +
    .icon-lianjie +
    +
  • + +
  • + +
    + 良品铺子 +
    +
    .icon-liangpinpuzi +
    +
  • + +
  • + +
    + 向下双箭头 +
    +
    .icon-xiangxiashuangjiantou +
    +
  • + +
  • + +
    + 月历 +
    +
    .icon-yueli +
    +
  • + +
  • + +
    + KHCFDC_音频文件 +
    +
    .icon-yinpinwenjian +
    +
  • + +
  • + +
    + excel +
    +
    .icon-excel +
    +
  • + +
  • + +
    + image +
    +
    .icon-image +
    +
  • + +
  • + +
    + pdf +
    +
    .icon-pdf +
    +
  • + +
  • + +
    + ppt +
    +
    .icon-ppt +
    +
  • + +
  • + +
    + txt +
    +
    .icon-txt +
    +
  • + +
  • + +
    + unknown +
    +
    .icon-unknown +
    +
  • + +
  • + +
    + word +
    +
    .icon-word +
    +
  • + +
  • + +
    + 默认文件夹 +
    +
    .icon-morenwenjianjia +
    +
  • + +
+
+

font-class 引用

+
+ +

font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。

+

与 Unicode 使用方式相比,具有如下特点:

+
    +
  • 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
  • +
  • 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
  • +
+

使用步骤如下:

+

第一步:引入项目下面生成的 fontclass 代码:

+
<link rel="stylesheet" href="./iconfont.css">
+
+

第二步:挑选相应图标并获取类名,应用于页面:

+
<span class="iconfont icon-xxx"></span>
+
+
+

" + iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

+
+
+
+
+
    + +
  • + +
    链接
    +
    #icon-lianjie
    +
  • + +
  • + +
    良品铺子
    +
    #icon-liangpinpuzi
    +
  • + +
  • + +
    向下双箭头
    +
    #icon-xiangxiashuangjiantou
    +
  • + +
  • + +
    月历
    +
    #icon-yueli
    +
  • + +
  • + +
    KHCFDC_音频文件
    +
    #icon-yinpinwenjian
    +
  • + +
  • + +
    excel
    +
    #icon-excel
    +
  • + +
  • + +
    image
    +
    #icon-image
    +
  • + +
  • + +
    pdf
    +
    #icon-pdf
    +
  • + +
  • + +
    ppt
    +
    #icon-ppt
    +
  • + +
  • + +
    txt
    +
    #icon-txt
    +
  • + +
  • + +
    unknown
    +
    #icon-unknown
    +
  • + +
  • + +
    word
    +
    #icon-word
    +
  • + +
  • + +
    默认文件夹
    +
    #icon-morenwenjianjia
    +
  • + +
+
+

Symbol 引用

+
+ +

这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章 + 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:

+
    +
  • 支持多色图标了,不再受单色限制。
  • +
  • 通过一些技巧,支持像字体那样,通过 font-size, color 来调整样式。
  • +
  • 兼容性较差,支持 IE9+,及现代浏览器。
  • +
  • 浏览器渲染 SVG 的性能一般,还不如 png。
  • +
+

使用步骤如下:

+

第一步:引入项目下面生成的 symbol 代码:

+
<script src="./iconfont.js"></script>
+
+

第二步:加入通用 CSS 代码(引入一次就行):

+
<style>
+.icon {
+  width: 1em;
+  height: 1em;
+  vertical-align: -0.15em;
+  fill: currentColor;
+  overflow: hidden;
+}
+</style>
+
+

第三步:挑选相应图标并获取类名,应用于页面:

+
<svg class="icon" aria-hidden="true">
+  <use xlink:href="#icon-xxx"></use>
+</svg>
+
+
+
+ +
+
+ + + diff --git a/src/assets/iconfont/iconfont.css b/src/assets/iconfont/iconfont.css new file mode 100644 index 0000000..305e10f --- /dev/null +++ b/src/assets/iconfont/iconfont.css @@ -0,0 +1,67 @@ +@font-face { + font-family: "iconfont"; /* Project id 4540741 */ + src: url('iconfont.woff2?t=1755072825017') format('woff2'), + url('iconfont.woff?t=1755072825017') format('woff'), + url('iconfont.ttf?t=1755072825017') format('truetype'); +} + +.iconfont { + font-family: "iconfont" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-lianjie:before { + content: "\e600"; +} + +.icon-liangpinpuzi:before { + content: "\e61f"; +} + +.icon-xiangxiashuangjiantou:before { + content: "\e62d"; +} + +.icon-yueli:before { + content: "\e6c4"; +} + +.icon-yinpinwenjian:before { + content: "\e6ed"; +} + +.icon-excel:before { + content: "\e8a6"; +} + +.icon-image:before { + content: "\e8aa"; +} + +.icon-pdf:before { + content: "\e8af"; +} + +.icon-ppt:before { + content: "\e8b0"; +} + +.icon-txt:before { + content: "\e8b4"; +} + +.icon-unknown:before { + content: "\e8b5"; +} + +.icon-word:before { + content: "\e8b6"; +} + +.icon-morenwenjianjia:before { + content: "\e6ea"; +} + diff --git a/src/assets/iconfont/iconfont.js b/src/assets/iconfont/iconfont.js new file mode 100644 index 0000000..356eb0f --- /dev/null +++ b/src/assets/iconfont/iconfont.js @@ -0,0 +1 @@ +window._iconfont_svg_string_4540741='',(l=>{var c=(h=(h=document.getElementsByTagName("script"))[h.length-1]).getAttribute("data-injectcss"),h=h.getAttribute("data-disable-injectsvg");if(!h){var v,t,i,a,e,o=function(c,h){h.parentNode.insertBefore(c,h)};if(c&&!l.__iconfont__svg__cssinject__){l.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}v=function(){var c,h=document.createElement("div");h.innerHTML=l._iconfont_svg_string_4540741,(h=h.getElementsByTagName("svg")[0])&&(h.setAttribute("aria-hidden","true"),h.style.position="absolute",h.style.width=0,h.style.height=0,h.style.overflow="hidden",h=h,(c=document.body).firstChild?o(h,c.firstChild):c.appendChild(h))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(v,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),v()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(i=v,a=l.document,e=!1,s(),a.onreadystatechange=function(){"complete"==a.readyState&&(a.onreadystatechange=null,n())})}function n(){e||(e=!0,i())}function s(){try{a.documentElement.doScroll("left")}catch(c){return void setTimeout(s,50)}n()}})(window); \ No newline at end of file diff --git a/src/assets/iconfont/iconfont.json b/src/assets/iconfont/iconfont.json new file mode 100644 index 0000000..12d865c --- /dev/null +++ b/src/assets/iconfont/iconfont.json @@ -0,0 +1,100 @@ +{ + "id": "4540741", + "name": "template-h5-stock", + "font_family": "iconfont", + "css_prefix_text": "icon-", + "description": "", + "glyphs": [ + { + "icon_id": "77403", + "name": "链接", + "font_class": "lianjie", + "unicode": "e600", + "unicode_decimal": 58880 + }, + { + "icon_id": "10033597", + "name": "良品铺子", + "font_class": "liangpinpuzi", + "unicode": "e61f", + "unicode_decimal": 58911 + }, + { + "icon_id": "14209038", + "name": "向下双箭头", + "font_class": "xiangxiashuangjiantou", + "unicode": "e62d", + "unicode_decimal": 58925 + }, + { + "icon_id": "18977410", + "name": "月历", + "font_class": "yueli", + "unicode": "e6c4", + "unicode_decimal": 59076 + }, + { + "icon_id": "9881679", + "name": "KHCFDC_音频文件", + "font_class": "yinpinwenjian", + "unicode": "e6ed", + "unicode_decimal": 59117 + }, + { + "icon_id": "1344630", + "name": "excel", + "font_class": "excel", + "unicode": "e8a6", + "unicode_decimal": 59558 + }, + { + "icon_id": "1344639", + "name": "image", + "font_class": "image", + "unicode": "e8aa", + "unicode_decimal": 59562 + }, + { + "icon_id": "1344646", + "name": "pdf", + "font_class": "pdf", + "unicode": "e8af", + "unicode_decimal": 59567 + }, + { + "icon_id": "1344647", + "name": "ppt", + "font_class": "ppt", + "unicode": "e8b0", + "unicode_decimal": 59568 + }, + { + "icon_id": "1344654", + "name": "txt", + "font_class": "txt", + "unicode": "e8b4", + "unicode_decimal": 59572 + }, + { + "icon_id": "1344655", + "name": "unknown", + "font_class": "unknown", + "unicode": "e8b5", + "unicode_decimal": 59573 + }, + { + "icon_id": "1344657", + "name": "word", + "font_class": "word", + "unicode": "e8b6", + "unicode_decimal": 59574 + }, + { + "icon_id": "22014791", + "name": "默认文件夹", + "font_class": "morenwenjianjia", + "unicode": "e6ea", + "unicode_decimal": 59114 + } + ] +} diff --git a/src/assets/iconfont/iconfont.ttf b/src/assets/iconfont/iconfont.ttf new file mode 100644 index 0000000..1b718a0 Binary files /dev/null and b/src/assets/iconfont/iconfont.ttf differ diff --git a/src/assets/iconfont/iconfont.woff b/src/assets/iconfont/iconfont.woff new file mode 100644 index 0000000..1f2c47e Binary files /dev/null and b/src/assets/iconfont/iconfont.woff differ diff --git a/src/assets/iconfont/iconfont.woff2 b/src/assets/iconfont/iconfont.woff2 new file mode 100644 index 0000000..6c5cfe7 Binary files /dev/null and b/src/assets/iconfont/iconfont.woff2 differ diff --git a/src/assets/index.scss b/src/assets/index.scss new file mode 100644 index 0000000..7d073ff --- /dev/null +++ b/src/assets/index.scss @@ -0,0 +1,502 @@ +.page-div { + .back-icon { + position: fixed; + top: 5px; + left: 5px; + z-index: 200; + @media screen and (min-width: 992px) { + left: calc(50vw - 491px) + } + &.tapped { + transform: scale(0.95); + opacity: 0.8; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + } + + i { + color: #fff; + font-size: 20px; + @media screen and (min-width: 768px) { + font-size: 30px; + } + } + } + + .header { + padding: 24px 16px 16px; + text-align: center; + background: linear-gradient(135deg, #165DFF 0%, #0FC6C2 100%); + color: white; + border-radius: 0 0 24px 24px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + margin-bottom: 16px; + + .title { + font-size: 22px; + font-weight: bold; + margin-bottom: 4px; + } + + .subtitle { + font-size: 14px; + opacity: 0.9; + } + + @media screen and (min-width: 768px) { + padding: 24px 16px 16px; + + .title { + font-size: 24px; + line-height: 1.2; + } + + .subtitle { + font-size: 16px; + line-height: 1.4; + } + } + } + + .info-card { + background-color: #fff; + padding: 16px; + border-radius: 16px; + margin: 0 16px 16px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); + + .card-title { + font-size: clamp(14px, 3.5vw, 16px); + font-weight: 600; + color: #333; + margin-bottom: 12px; + padding-bottom: 8px; + border-bottom: 1px solid #f0f0f0; + } + + @media screen and (min-width: 768px) { + padding: 20px; + margin: 0 16px 16px; + + .card-title { + font-size: 18px; + margin-bottom: 10px; + padding-bottom: 6px; + line-height: 1.4; + } + + .info-text { + font-size: 14px; + line-height: 1.6; + } + } + + .info-text { + font-size: 14px; + color: #666; + line-height: 1.6; + } + + .chart-container { + display: flex; + align-items: center; + justify-content: center; + + .loading { + position: absolute; + z-index: 2; + } + + .chartDiv { + width: 100%; + height: 100%; + } + } + .tab-container { + width: 100%; + display: flex; + background-color: #ffffff; + box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05); + transition: all 0.3s ease; + + .tab-button { + flex: 1; + font-size: clamp(12px, 3vw, 14px); + padding: 12px 0; + transition: all 0.2s ease; + background: none; + border: none; + outline: none; + cursor: pointer; + color: #666; + } + + .tab-button.active { + color: #165DFF; + font-weight: 500; + border-bottom: 2px solid #165DFF; + } + } + } + + .grid { + height: 100%; + overflow-y: auto; + flex: 1; + + .van-grid-item__content { + border-radius: 16px; + + .item { + width: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background-color: #ffffff; + + &.tapped { + transform: scale(0.9); + opacity: 0.7; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); + background-color: rgba(240, 240, 240, 0.5); + } + + .card-content { + height: 100%; + width: 100%; + padding: 16px; + border-radius: 16px; + border-left: 4px solid; + transition: all 0.3s ease; + display: flex; + flex-direction: column; + justify-content: space-between; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); + + .card-code { + font-size: 16px; + font-weight: bold; + margin-bottom: 6px; + } + + .card-name { + font-size: 14px; + color: #333; + font-weight: 500; + margin-bottom: 4px; + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + overflow: hidden; + } + + .card-note { + font-size: 12px; + color: #666; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + flex-grow: 1; + } + } + + + @media screen and (min-width: 768px) { + padding: 18px; + + .card-code { + font-size: 18px; + line-height: 1.3; + } + + .card-name { + font-size: 15px; + margin-bottom: 7px; + } + + .card-note { + font-size: 13px; + line-height: 1.5; + } + + .card-name { + font-size: 15px; + } + + .card-note { + font-size: 14px; + } + } + } + } + } +} + +.icon-morenwenjianjia, +.icon-folder6wenjianjia { + color: #72cffb; +} +.icon-ppt { + color: #e34221; +} +.icon-pdf { + color: #8c181a; +} +.icon-excel { + color: #45b058; +} +.icon-txt { + color: #f9ca06; +} +.icon-word { + color: #14a9da; +} +.icon-unknown { + color: #7d99af; +} +.icon-image { + color: #49c9a7; +} + +.red-color { + color: red !important; +} + +.blue-color { + color: blue !important; +} + +.green-color { + color: green !important; +} + +.mt-16 { + margin-top: 16px; +} + +.financial-data { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 12px 8px; +} + +.data-item { + display: flex; + flex-direction: column; + padding: 8px; + background-color: #f9fafb; + border-radius: 8px; +} + +.data-label { + font-size: 12px; + color: #666; + margin-bottom: 4px; +} + +.data-value { + font-size: 14px; + font-weight: 500; + color: #333; + display: flex; + align-items: center; +} + +@media screen and (min-width: 768px) { + .financial-data { + grid-template-columns: repeat(5, 1fr); + } + .data-item { + padding: 12px; + } + .data-label { + font-size: 13px; + } + .data-value { + font-size: 15px; + } +} + +table { + width: 100%; + border-spacing: 0; + text-align: center; +} + +.tableDiv { + overflow: auto; + table { + border: 1px solid #808080; + border-collapse: collapse; + width: 100%; + thead { + text-align: center; + + tr { + th { + border: 1px solid #757575; + font-weight: 400; + padding: 0; + box-sizing: border-box; + position: sticky; + top: -1px; + background-color: #fff; + z-index: 2; + width: 100px; + + @media screen and (min-width: 768px) { + font-size: 13px; + line-height: 1.5; + } + + &:first-child { + position: sticky; + left: -1px; + z-index: 3; + } + } + } + } + + tbody { + text-align: center; + + tr { + td { + border: 1px solid #757575; + box-sizing: border-box; + background-color: #fff; + z-index: 2; + + @media screen and (min-width: 768px) { + font-size: 13px; + line-height: 1.5; + } + + &.td--column--1 { + position: sticky; + left: -1px; + background-color: #fff; + z-index: 3; + } + } + } + } + tbody { + text-align: center; + + tr { + td { + border: 1px solid #757575; + box-sizing: border-box; + background-color: #fff; + z-index: 2; + + &.td--column--1 { + position: sticky; + left: -1px; + background-color: #fff; + z-index: 3; + } + } + } + } + } +} + +.d-flex { + display: flex; +} + +.flex-1 { + flex: 1; +} + +.align-center { + align-items: center; +} + +.align-end { + align-items: flex-end; +} + +.align-start { + align-items: flex-start; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.justify-end { + justify-content: flex-end; +} + +.text-start { + text-align: left; +} + +.text-center { + text-align: center; +} + +.text-end { + text-align: right; +} + +.f-b { + font-weight: bold; +} + +.big-title { + font-size: 20px; + @media screen and (min-width: 768px) { + font-size: 24px; + line-height: 1.3; + } +} + +.normal-title { + font-size: 18px; +} + +.sub-title { + font-size: 16px; +} + +.tip { + font-size: 12px; +} + +.p-16 { + padding: 16px; +} + +.pt-16 { + padding-top: 16px; +} + +.pb-16 { + padding-bottom: 16px; +} + +.mt-16 { + margin-top: 16px; +} + +.mb-16 { + margin-bottom: 16px; +} + +.mb-8 { + margin-bottom: 8px; +} + +.mb-5 { + margin-bottom: 5px; +} \ No newline at end of file diff --git a/src/assets/logo.svg b/src/assets/logo.svg new file mode 100644 index 0000000..7565660 --- /dev/null +++ b/src/assets/logo.svg @@ -0,0 +1 @@ + diff --git a/src/components/back-button.vue b/src/components/back-button.vue new file mode 100644 index 0000000..2eee3b9 --- /dev/null +++ b/src/components/back-button.vue @@ -0,0 +1,39 @@ + + + + + \ No newline at end of file diff --git a/src/components/communicate-item.vue b/src/components/communicate-item.vue new file mode 100644 index 0000000..f68a065 --- /dev/null +++ b/src/components/communicate-item.vue @@ -0,0 +1,58 @@ + + + + \ No newline at end of file diff --git a/src/components/file-list.vue b/src/components/file-list.vue new file mode 100644 index 0000000..eb2a585 --- /dev/null +++ b/src/components/file-list.vue @@ -0,0 +1,140 @@ + + + + diff --git a/src/components/tabbar.vue b/src/components/tabbar.vue new file mode 100644 index 0000000..1c76095 --- /dev/null +++ b/src/components/tabbar.vue @@ -0,0 +1,21 @@ + + + + diff --git a/src/components/top-date1.vue b/src/components/top-date1.vue new file mode 100644 index 0000000..1bb38da --- /dev/null +++ b/src/components/top-date1.vue @@ -0,0 +1,44 @@ + + + \ No newline at end of file diff --git a/src/components/top-select1.vue b/src/components/top-select1.vue new file mode 100644 index 0000000..a1a92eb --- /dev/null +++ b/src/components/top-select1.vue @@ -0,0 +1,118 @@ + + + \ No newline at end of file diff --git a/src/components/top-select2.vue b/src/components/top-select2.vue new file mode 100644 index 0000000..66cb48f --- /dev/null +++ b/src/components/top-select2.vue @@ -0,0 +1,90 @@ + + + \ No newline at end of file diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..89efe78 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,31 @@ +import './assets/iconfont/iconfont.css' +import './assets/base.css' +import './assets/index.scss' +import 'vant/es/toast/style' +import 'vant/es/dialog/style' +import '@vant/touch-emulator' + +import { createApp } from 'vue' +import pinia from './stores/index' +import wx from 'weixin-js-sdk' +import { wxMpJsapiGetJsapiTicket } from '@/utils/api' +import VueWechatTitle from 'vue-wechat-title' +wxMpJsapiGetJsapiTicket({ url: window.location.href }).then(({ data }) => { + console.log(data) + wx.config({ + appId: 'wx1d52f91f259a691f', + timestamp: data.timestamp, + nonceStr: data.nonceStr, + signature: data.signature, + jsApiList: ['updateAppMessageShareData'] + }) +}) +import App from './App.vue' +import router from './router' +const app = createApp(App) +app.use(VueWechatTitle) +app.use(router) +app.use(pinia) + +//Vue3挂载全局API +app.mount('#app') diff --git a/src/mixins/chart-mixins.ts b/src/mixins/chart-mixins.ts new file mode 100644 index 0000000..2de5203 --- /dev/null +++ b/src/mixins/chart-mixins.ts @@ -0,0 +1,41 @@ +import { ref, onMounted, onBeforeUnmount, nextTick, type Ref } from 'vue' +import type { EChartsType } from 'echarts' +export function chartMixins() { + const charts: Ref<(EChartsType | null)[]> = ref([]) + const destroyedFlag: Ref = ref(false) + + const disposeAll = () => { + charts.value.map((chart) => { + if (chart) { + chart.dispose() + chart = null + } + }) + } + + const changeChartSize = () => { + nextTick(() => { + charts.value.forEach((chart) => { + if (chart) { + chart.resize() + } + }) + }) + } + + onMounted(() => { + window.addEventListener('resize', changeChartSize) + }) + + onBeforeUnmount(() => { + destroyedFlag.value = true + window.removeEventListener('resize', changeChartSize) + disposeAll() + }) + + return { + charts, + disposeAll, + destroyedFlag, + } +} diff --git a/src/mixins/debounce-throttle.ts b/src/mixins/debounce-throttle.ts new file mode 100644 index 0000000..887be6b --- /dev/null +++ b/src/mixins/debounce-throttle.ts @@ -0,0 +1,40 @@ +export function debounceThrottle() { + // 防抖 + function debounce(fn: any, delay: number) { + let timer: any = null + return function () { + // 保留调用时的this上下文 + const context = this + // 保留调用时传入的参数 + const args: any = arguments + // 每次事件被触发时,都去清除之前的旧定时器 + clearTimeout(timer) + // 设立新定时器 + timer = setTimeout(() => { + fn.apply(context, args) + }, delay) + } + } + + // 节流 + function throttle(fn: any, delay: number) { + let timer: any = null + return function () { + // 保留调用时的this上下文 + const context = this + // 保留调用时传入的参数 + const args: any = arguments + if (timer) { + return + } + timer = setTimeout(() => { + fn.apply(context, args) + timer = null + }, delay) + } + } + return { + debounce, + throttle + } +} diff --git a/src/mixins/list-load-and-refresh.ts b/src/mixins/list-load-and-refresh.ts new file mode 100644 index 0000000..33c9aaf --- /dev/null +++ b/src/mixins/list-load-and-refresh.ts @@ -0,0 +1,65 @@ +import { ref } from 'vue' + +import emitter from '@/utils/mitt' + +export function listLoadAndRefresh(getData: any, type: string, scrollPage: any = {}) { + const refreshing = ref(false) + const finished = ref(false) + const curPage = ref(1) + const loading = ref(false) + const list: any = ref([]) + const totalCount = ref(0) + const firstFlag = ref(true) + + async function onLoad() { + if (firstFlag.value) { + emitter.emit('showLoading', '') + } + loading.value = true + try { + const data = await getData(curPage.value) + totalCount.value = data.totalCount + if (refreshing.value) { + list.value = data.list + refreshing.value = false + } else { + list.value = list.value.concat(data.list) + } + if (list.value.length >= data.totalCount) { + finished.value = true + } else { + curPage.value++ + } + } catch (error) { + console.log(error) + } + if (type === 'internal' || type === 'report') { + loading.value = false + scrollPage() + } else { + loading.value = false + if (firstFlag.value) { + emitter.emit('hiddenLoading', '') + firstFlag.value = false + } + } + } + + function onRefresh() { + // list.value = [] + refreshing.value = true + finished.value = false + curPage.value = 1 + onLoad() + } + return { + refreshing, + finished, + loading, + list, + onLoad, + onRefresh, + curPage, + totalCount + } +} diff --git a/src/mixins/resize-listener-and-swipe-indicator.ts b/src/mixins/resize-listener-and-swipe-indicator.ts new file mode 100644 index 0000000..89c7258 --- /dev/null +++ b/src/mixins/resize-listener-and-swipe-indicator.ts @@ -0,0 +1,104 @@ +import { ref, onMounted, onUnmounted, nextTick } from 'vue' + +export const resizeListenerAndSwipeIndicator = ( + charts: any, + getData: any +) => { + const horizontalFlag = ref(true) + const active = ref(0) + const swipe = ref() + const horizontalDiv = ref() + onMounted(() => { + console.log(swipe.value); + + if (horizontalDiv.value) { + horizontalDiv.value.addEventListener('touchstart', handleTouchStart) + horizontalDiv.value.addEventListener('touchmove', handleTouchMove) + horizontalDiv.value.addEventListener('touchend', handleTouchEnd) + } + }) + + onUnmounted(() => { + disposeAll() + if (horizontalDiv.value) { + horizontalDiv.value.removeEventListener('touchstart', handleTouchStart) + horizontalDiv.value.removeEventListener('touchmove', handleTouchMove) + horizontalDiv.value.removeEventListener('touchend', handleTouchEnd) + } + }) + + let startX = 0 + let startY = 0 + let isSwiping = false + + const handleTouchStart = (event: TouchEvent) => { + if (horizontalFlag.value) return + const touch = event.touches[0] + startX = touch.pageX + startY = touch.pageY + isSwiping = true + } + + const handleTouchMove = (event: TouchEvent) => { + if (!isSwiping || horizontalFlag.value) return + const touch = event.touches[0] + const deltaX = touch.pageX - startX + const deltaY = touch.pageY - startY + if (Math.abs(deltaX) > Math.abs(deltaY)) { + if (deltaX > 0) { + next() + } else if (deltaX < 0) { + pre() + } + isSwiping = false + } + } + + const handleTouchEnd = () => { + if (horizontalFlag.value) { + isSwiping = false + } + } + + const disposeAll = () => { + charts.value.forEach((chart: any) => { + chart && chart.dispose() + chart = null + }) + } + const onChange = (e: number) => { + active.value = e + const chart = charts.value[e] + chart && chart.dispose() + getData(active.value) + } + const showHorizontalChart = () => { + horizontalFlag.value = !horizontalFlag.value + if (!swipe.value) { + nextTick(() => { + charts.value[active.value].resize() + }) + } else { + disposeAll() + swipe.value && swipe.value.resize() + getData(active.value) + } + } + const next = () => { + swipe.value.next() + } + const pre = () => { + swipe.value.prev() + } + return { + onChange, + active, + next, + pre, + swipe, + disposeAll, + showHorizontalChart, + horizontalFlag, + horizontalDiv + } +} diff --git a/src/mixins/scroll-list.ts b/src/mixins/scroll-list.ts new file mode 100644 index 0000000..8601cda --- /dev/null +++ b/src/mixins/scroll-list.ts @@ -0,0 +1,59 @@ +import { ref, onActivated, nextTick } from 'vue' +import listRefreshInfoStore from '@/stores/listRefreshInfo' +const listRefreshInfo = listRefreshInfoStore() + +export function scrollList(pageContent: any, scrollPosition: any, refresh: any) { + const refreshFlag = ref(true) + function setScrollTop() { + console.log(1) + onActivated(() => { + nextTick(() => { + if (listRefreshInfo.refreshId) { + refresh('refresh', listRefreshInfo.refreshId) + listRefreshInfo.refreshId = '' + } else if (listRefreshInfo.deleteId) { + refresh('delete', listRefreshInfo.deleteId) + listRefreshInfo.deleteId = '' + } else if (listRefreshInfo.addId) { + listRefreshInfo.addId = '' + refresh() + } else { + if (scrollPosition.value && pageContent.value) { + pageContent.value.scrollTop = scrollPosition.value + } + if (refreshFlag.value === true) { + refresh() + } + } + }) + }) + } + + function setScrollPositionAndRefreshFlag(to: any, page: string) { + if (pageContent.value) { + scrollPosition.value = pageContent.value.scrollTop + } + if (page.includes(',')) { + const pages = page.split(',') + const index = pages.findIndex((ele: any) => { + return to.name === ele + }) + if (index > -1) { + refreshFlag.value = false + } else { + refreshFlag.value = true + } + } else { + if (to.name === page) { + refreshFlag.value = false + } else { + refreshFlag.value = true + } + } + } + + return { + setScrollTop, + setScrollPositionAndRefreshFlag + } +} diff --git a/src/mixins/show-file.ts b/src/mixins/show-file.ts new file mode 100644 index 0000000..400866b --- /dev/null +++ b/src/mixins/show-file.ts @@ -0,0 +1,14 @@ +export function showFile() { + function loadingFile(url: any) { + const link = document.createElement('a') + link.style.display = 'none' + link.href = url + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + } + + return { + loadingFile + } +} diff --git a/src/mixins/tap-mixins.ts b/src/mixins/tap-mixins.ts new file mode 100644 index 0000000..3d0a424 --- /dev/null +++ b/src/mixins/tap-mixins.ts @@ -0,0 +1,48 @@ +import { ref } from 'vue' +import { useRouter } from 'vue-router' + +export function tapMixins() { + const tappedItem = ref(null) + const isProcessing = ref(false) + const router = useRouter() + + const onTouchStart = (name: string) => { + if (isProcessing.value) return + tappedItem.value = name + } + + const onTouchEnd = (name: string, url: string, id?: number, delay: number = 150) => { + if (isProcessing.value || tappedItem.value !== name) return + isProcessing.value = true + + setTimeout(() => { + tappedItem.value = null + setTimeout(() => { + if (url) { + if (id) { + router.push({ name: url, params: { id } }) + } else { + router.push({ name: url }) + } + } else { + showToast('正在开发中') + } + isProcessing.value = false + }, 50) + }, delay) + } + + const onTouchCancel = (name: string) => { + if (tappedItem.value === name) { + tappedItem.value = null + } + } + + return { + tappedItem, + isProcessing, + onTouchStart, + onTouchEnd, + onTouchCancel + } +} \ No newline at end of file diff --git a/src/router/index.ts b/src/router/index.ts new file mode 100644 index 0000000..03e3c9f --- /dev/null +++ b/src/router/index.ts @@ -0,0 +1,246 @@ +import { createRouter, createWebHistory } from 'vue-router' +import userInfoStore from '@/stores/userInfo' + +const router = createRouter({ + history: createWebHistory(), + routes: [ + { + path: '/404', + name: '404', + component: () => import('../views/common/error.vue'), + meta: { title: '404未找到', isPass: true } + }, + { + path: '/login', + name: 'login', + component: () => import('../views/common/login.vue'), + meta: { title: '登录', isPass: true } + }, + { + path: '/loading-page', + name: 'loading-page', + component: () => import('../views/common/loading-page.vue'), + meta: { title: '加载', isPass: true } + }, + { + path: '/organization-list', + name: 'organization-list', + component: () => import('../views/organization/list.vue'), + meta: { title: '机构列表', keepAlive: true } + }, + { + path: '/organization-detail', + name: 'organization-detail', + component: () => import('../views/organization/detail.vue'), + meta: { title: '机构详情' } + }, + { + path: '/comment-list', + name: 'comment-list', + component: () => import('../views/comment/list.vue'), + meta: { title: '点评列表', keepAlive: true } + }, + { + path: '/comment-detail', + name: 'comment-detail', + component: () => import('../views/comment/detail.vue'), + meta: { title: '点评详情' } + }, + { + path: '/meeting-list', + name: 'meeting-list', + component: () => import('../views/meeting/list.vue'), + meta: { title: '会议列表', keepAlive: true } + }, + { + path: '/meeting-detail', + name: 'meeting-detail', + component: () => import('../views/meeting/detail.vue'), + meta: { title: '会议详情' } + }, + { + path: '/add-or-update-meeting', + name: 'add-or-update-meeting', + component: () => import('../views/meeting/add-or-update-meeting.vue'), + meta: { title: '会议' } + }, + { + path: '/communicate-list', + name: 'communicate-list', + component: () => import('../views/communicate/list.vue'), + meta: { title: '沟通列表', keepAlive: true } + }, + { + path: '/communicate-detail', + name: 'communicate-detail', + component: () => import('../views/communicate/detail.vue'), + meta: { title: '沟通详情' } + }, + { + path: '/add-cus', + name: 'add-cus', + component: () => import('../views/communicate/add-cus.vue'), + meta: { title: '添加机构' } + }, + { + path: '/add-cusUser', + name: 'add-cusUser', + component: () => import('../views/communicate/add-cusUser.vue'), + meta: { title: '添加联系人' } + }, + { + path: '/report-list', + name: 'report-list', + component: () => import('../views/report/list.vue'), + meta: { title: '报告列表' } + }, + { + path: '/stock-list', + name: 'stock-list', + component: () => import('../views/stock/list.vue'), + meta: { title: '股票动态', keepAlive: true } + }, + { + path: '/stock-detail', + name: 'stock-detail', + component: () => import('../views/stock/detail.vue'), + meta: { title: '股票详情', keepAlive: true } + }, + { + path: '/target-table', + name: 'target-table', + component: () => import('../views/target/table.vue'), + meta: { title: '重要指标', keepAlive: true, isPass: true } + }, + { + path: '/economy-list', + name: 'economy-list', + component: () => import('../views/target/economy/economy-list.vue'), + meta: { title: '经济专题', keepAlive: true } + }, + { + path: '/economy-detail/:id?', + name: 'economy-detail', + component: () => import('../views/target/economy/economy-detail.vue'), + meta: { title: '专题详情' } + }, + { + path: '/fred-list', + name: 'fred-list', + component: () => import('../views/target/fred/fred-list.vue'), + meta: { title: 'FRED专题', keepAlive: true } + }, + { + path: '/fred-detail/:id?', + name: 'fred-detail', + component: () => import('../views/target/fred/fred-detail.vue'), + meta: { title: 'FRED详情' } + }, + { + path: '/series-list', + name: 'series-list', + component: () => import('../views/target/series/series-list.vue'), + meta: { title: '指数系列', keepAlive: true } + }, + { + path: '/index-list/:id?', + name: 'index-list', + component: () => import('../views/target/index/index-list.vue'), + meta: { title: '指数专题', keepAlive: true } + }, + { + path: '/index-detail/:id?', + name: 'index-detail', + component: () => import('../views/target/index/index-detail.vue'), + meta: { title: '指数详情', keepAlive: true } + }, + { + path: '/etf-detail/:id?', + name: 'etf-detail', + component: () => import('../views/target/etf/etf-detail.vue'), + meta: { title: 'ETF详情' } + }, + { + path: '/fund-list', + name: 'fund-list', + component: () => import('../views/target/fund/fund-list.vue'), + meta: { title: '基金列表', keepAlive: true } + }, + { + path: '/fund-detail/:id?', + name: 'fund-detail', + component: () => import('../views/target/fund/fund-detail.vue'), + meta: { title: '基金详情' } + }, + { + path: '/group-list', + name: 'group-list', + component: () => import('../views/target/group/group-list.vue'), + meta: { title: '精选组合', keepAlive: true } + }, + { + path: '/group-detail/:id?', + name: 'group-detail', + component: () => import('../views/target/group/group-detail.vue'), + meta: { title: '组合详情' } + }, + { + path: '/internal-meeting', + name: 'internal-meeting', + component: () => import('../views/internal/meeting.vue'), + meta: { title: '内部会议' } + }, + { + path: '/subscription', + name: 'subscription', + component: () => import('../views/common/subscription.vue'), + meta: { title: '订阅' } + }, + { + path: '/flow-company', + name: 'flow-company', + component: () => import('../views/flow/company.vue'), + meta: { title: '重点公司', keepAlive: true } + }, + { + path: '/flow', + name: 'flow', + component: () => import('../views/flow/flow.vue'), + meta: { title: '重点公司追踪', keepAlive: true } + }, + { + path: '/flow-detail', + name: 'flow-detail', + component: () => import('../views/flow/flow-detail.vue'), + meta: { title: '重点公司追踪详情' } + }, + { + path: '/user', + name: 'user', + component: () => import('../views/user/index.vue'), + meta: { title: '个人中心', isPass: true } + }, + { + path: '/log', + name: 'log', + component: () => import('../views/common/log.vue'), + meta: { title: '日志详情', isPass: true } + }, + { + path: '/', + name: 'home', + component: () => import('../views/home/index.vue'), + meta: { title: '首页', isPass: true } + } + ] +}) + +router.beforeEach((to, from, next) => { + const userInfo = userInfoStore() + if (!to.meta.isPass && !userInfo.token ) { + return next({ name: 'login' }) + } + next() +}) + +export default router diff --git a/src/stores/etfStockInfo.ts b/src/stores/etfStockInfo.ts new file mode 100644 index 0000000..bf2f1d0 --- /dev/null +++ b/src/stores/etfStockInfo.ts @@ -0,0 +1,17 @@ +import { defineStore } from 'pinia' +export default defineStore('etfStockInfo', { + state: () => ({ + stockCode: '', + companyList: [], + xAxisData: [] + }), + persist: { + enabled: true, + strategies: [ + { + key: 'etfStockInfo', // 持久化状态的键 + storage: localStorage // 使用localStorage来持久化状态 + } + ] + } +}) diff --git a/src/stores/fileInfo.ts b/src/stores/fileInfo.ts new file mode 100644 index 0000000..8e860e3 --- /dev/null +++ b/src/stores/fileInfo.ts @@ -0,0 +1,15 @@ +import { defineStore } from 'pinia' +export default defineStore('fileInfo', { + state: () => ({ + fileId: '' + }), + persist: { + enabled: true, + strategies: [ + { + key: 'fileInfo', // 持久化状态的键 + storage: localStorage // 使用localStorage来持久化状态 + } + ] + } +}) diff --git a/src/stores/index.ts b/src/stores/index.ts new file mode 100644 index 0000000..c3c85c2 --- /dev/null +++ b/src/stores/index.ts @@ -0,0 +1,38 @@ +import { createPinia } from 'pinia' +import persistedState from 'pinia-plugin-persistedstate' +import SecureLS from 'secure-ls' +export const ls = new SecureLS({ + encodingType: 'aes', + isCompression: false +}) +// 创建 +const pinia = createPinia() +pinia.use(persistedState) +pinia.use(({ store }) => { + // 加密状态并存储到 SecureLS 中 + const encryptAndStoreState = () => { + ls.set(store.$id, store.$state) + } + + // 解密状态并恢复到 Pinia 中 + const decryptAndRestoreState = () => { + try { + const data = ls.get(store.$id) + if (data) { + store.$state = data + } + } catch (error) { + console.error('Error decrypting and restoring state:', error) + // 处理异常情况,比如重新初始化状态或显示错误信息 + store.$reset() + } + } + + // 在每次状态变更时调用加密函数 + store.$subscribe(encryptAndStoreState) + + // 在初始化时调用解密函数 + decryptAndRestoreState() +}) +// 导出 +export default pinia diff --git a/src/stores/listRefreshInfo.ts b/src/stores/listRefreshInfo.ts new file mode 100644 index 0000000..864b024 --- /dev/null +++ b/src/stores/listRefreshInfo.ts @@ -0,0 +1,17 @@ +import { defineStore } from 'pinia' +export default defineStore('fileInfo', { + state: () => ({ + refreshId: '', + deleteId: '', + addId: '' + }), + persist: { + enabled: true, + strategies: [ + { + key: 'fileInfo', // 持久化状态的键 + storage: localStorage // 使用localStorage来持久化状态 + } + ] + } +}) diff --git a/src/stores/reportInfo.ts b/src/stores/reportInfo.ts new file mode 100644 index 0000000..7468560 --- /dev/null +++ b/src/stores/reportInfo.ts @@ -0,0 +1,21 @@ +import { defineStore } from 'pinia' +export default defineStore('speakInfo', { + state: () => ({ + reportId: '', + index: 0, + stockCode: '', + stockName: '股票', + date: '日期', + reportTypeName: '类型', + reportType: '' + }), + persist: { + enabled: true, + strategies: [ + { + key: 'speakInfo', // 持久化状态的键 + storage: localStorage // 使用localStorage来持久化状态 + } + ] + } +}) diff --git a/src/stores/saveCommunicateInfo.ts b/src/stores/saveCommunicateInfo.ts new file mode 100644 index 0000000..44454a1 --- /dev/null +++ b/src/stores/saveCommunicateInfo.ts @@ -0,0 +1,45 @@ +import { defineStore } from 'pinia' +export default defineStore('saveCommunicateInfo', { + state: () => ({ + linkTypeName: '', + selectlinkType: '', + dicNote2: '', + dicNote3: '', + sysDay: '', + selectsysDay: [] as string[], + sysTime: '', + selectsysTime: [] as string[], + cusName: '', + selectcus: '', + cusLevelName: '', + selectcusLevel: '', + linkTitle: '', + cusUserNames: '', + selectcusUserIds: [], + staffNames: '', + selectstaffIds: [], + saleNames: '', + selectsaleIds: [], + linkFromName: '', + linkFrom: 1, + linkWayName: '', + selectlinkWay: '', + linkNote: '', + meetingName: '', + selectmeeting: '', + staffNote: '', + cusLinkStockList: [], + cusLinkStockListForLinkId: [], + saleScore: 0, + saleNote: '' + }), + persist: { + enabled: true, + strategies: [ + { + key: 'saveCommunicateInfo', // 持久化状态的键 + storage: localStorage // 使用localStorage来持久化状态 + } + ] + } +}) diff --git a/src/stores/saveCusInfo.ts b/src/stores/saveCusInfo.ts new file mode 100644 index 0000000..b825b46 --- /dev/null +++ b/src/stores/saveCusInfo.ts @@ -0,0 +1,45 @@ +import { defineStore } from 'pinia' +export default defineStore('saveCusInfo', { + state: () => ({ + cusName: '', + cusLevelName: '', + selectcusLevel: '', + cusUserName: '', + selectcusUser: '', + companyTypeName: '', + selectcompanyType: '', + cityName: '', + selectcity: '', + address: '', + accountStatus: '1', + accountTime: '', + selectaccountTime: [] as string[], + accountTypeNames: '', + selectaccountTypeIds: [], + isMoney: '0', + tradeMoney: '', + capitalScaleName: '', + selectcapitalScale: '', + capitalScaleValue: '', + fundNum: '', + investOverseasPer: '', + investResearchNum: '', + investChannelName: '', + selectinvestChannel: '', + website: '', + companyCreateTime: '', + selectcompanyCreateTime: [] as string[], + introduction: '', + saleUserNames: '', + selectsaleUserIds: [] + }), + persist: { + enabled: true, + strategies: [ + { + key: 'saveCusInfo', // 持久化状态的键 + storage: localStorage // 使用localStorage来持久化状态 + } + ] + } +}) diff --git a/src/stores/saveInfo.ts b/src/stores/saveInfo.ts new file mode 100644 index 0000000..46b4549 --- /dev/null +++ b/src/stores/saveInfo.ts @@ -0,0 +1,19 @@ +import { defineStore } from 'pinia' +export default defineStore('speakInfo', { + state: () => ({ + id: '', + name: 0, + type: '', + positionName: '', + day: '' + }), + persist: { + enabled: true, + strategies: [ + { + key: 'speakInfo', // 持久化状态的键 + storage: localStorage // 使用localStorage来持久化状态 + } + ] + } +}) diff --git a/src/stores/speakInfo.ts b/src/stores/speakInfo.ts new file mode 100644 index 0000000..bcd6574 --- /dev/null +++ b/src/stores/speakInfo.ts @@ -0,0 +1,18 @@ +import { defineStore } from 'pinia' +export default defineStore('speakInfo', { + state: () => ({ + speakId: '', + index: 0, + speakType: '', + speakTypeName: '' + }), + persist: { + enabled: true, + strategies: [ + { + key: 'speakInfo', // 持久化状态的键 + storage: localStorage // 使用localStorage来持久化状态 + } + ] + } +}) diff --git a/src/stores/stockInfo.ts b/src/stores/stockInfo.ts new file mode 100644 index 0000000..1b73108 --- /dev/null +++ b/src/stores/stockInfo.ts @@ -0,0 +1,15 @@ +import { defineStore } from 'pinia' +export default defineStore('stockInfo', { + state: () => ({ + id: '' + }), + persist: { + enabled: true, + strategies: [ + { + key: 'stockInfo', // 持久化状态的键 + storage: localStorage // 使用localStorage来持久化状态 + } + ] + } +}) diff --git a/src/stores/updateCommunicateInfo.ts b/src/stores/updateCommunicateInfo.ts new file mode 100644 index 0000000..894859c --- /dev/null +++ b/src/stores/updateCommunicateInfo.ts @@ -0,0 +1,45 @@ +import { defineStore } from 'pinia' +export default defineStore('updateCommunicateInfo', { + state: () => ({ + linkTypeName: '', + selectlinkType: '', + dicNote2: '', + dicNote3: '', + sysDay: '', + selectsysDay: [] as string[], + sysTime: '', + selectsysTime: [] as string[], + cusName: '', + selectcus: '', + cusLevelName: '', + selectcusLevel: '', + linkTitle: '', + cusUserNames: '', + selectcusUserIds: [], + staffNames: '', + selectstaffIds: [], + saleNames: '', + selectsaleIds: [], + linkFromName: '', + linkFrom: 1, + linkWayName: '', + selectlinkWay: '', + linkNote: '', + meetingName: '', + selectmeeting: '', + staffNote: '', + cusLinkStockList: [], + cusLinkStockListForLinkId: [], + saleScore: 0, + saleNote: '' + }), + persist: { + enabled: true, + strategies: [ + { + key: 'updateCommunicateInfo', // 持久化状态的键 + storage: localStorage // 使用localStorage来持久化状态 + } + ] + } +}) diff --git a/src/stores/userInfo.ts b/src/stores/userInfo.ts new file mode 100644 index 0000000..76cb120 --- /dev/null +++ b/src/stores/userInfo.ts @@ -0,0 +1,22 @@ +import { defineStore } from 'pinia' +export default defineStore('userInfo', { + state: () => ({ + permissions: [] as string[], + token: '', + id: '', + username: '', + password: '', + roleId: '', + realname: '', + headPic: '' + }), + persist: { + enabled: true, + strategies: [ + { + key: 'userInfo', // 持久化状态的键 + storage: localStorage // 使用localStorage来持久化状态 + } + ] + } +}) diff --git a/src/types/vue-virtual-scroller.d.ts b/src/types/vue-virtual-scroller.d.ts new file mode 100644 index 0000000..6d6370c --- /dev/null +++ b/src/types/vue-virtual-scroller.d.ts @@ -0,0 +1 @@ +declare module 'vue-virtual-scroller'; \ No newline at end of file diff --git a/src/utils/api.ts b/src/utils/api.ts new file mode 100644 index 0000000..1553820 --- /dev/null +++ b/src/utils/api.ts @@ -0,0 +1,818 @@ +import http from './request' + +/** 系统 **/ +// 登录微信公众号 +export function wxMpJsapiGetJsapiTicket(params: object) { + return http.request({ + url: '/wx/mp/jsapi/getJsapiTicket', + method: 'get', + params: params + }) +} + +export function sysLog(id: number) { + return http.request({ + url: `/sys/user/log/app/info/${id}`, + method: 'get', + }) +} +// 登录 +export function sysLogin(data: object) { + return http.request({ + url: '/sys/login', + method: 'post', + data: data + }) +} + +export function sysMenuNav() { + return http.request({ + url: '/sys/menu/nav', + method: 'get' + }) +} + +// 获取用户信息 +export function sysUserInfo() { + return http.request({ + url: '/sys/user/info', + method: 'get' + }) +} + +// 获取研究员和管理层 +export function sysUserGetStaffAndManagerList() { + return http.request({ + url: '/sys/user/getStaffAndManagerList', + method: 'get' + }) +} + +// 获取销售和管理层 +export function sysUserGetSaleAndManagerList() { + return http.request({ + url: '/sys/user/getSaleAndManagerList', + method: 'get' + }) +} + +// 员工列表 +export function workStaffLookup() { + return http.request({ + url: '/work/staff/lookup', + method: 'get' + }) +} + +// 获取全公司 +export function sysUserGetCompanyUserList() { + return http.request({ + url: '/sys/user/getCompanyUserList', + method: 'get' + }) +} + +// 根据类型获取字典 +export function sysDicListByType(params: object) { + return http.request({ + url: '/sys/dic/listByType', + method: 'get', + params: params + }) +} + +// 根据类型和note获取字典 +export function sysDicListByNote(params: object) { + return http.request({ + url: '/sys/dic/listByNote', + method: 'get', + params: params + }) +} + +/** 点评 **/ +// 点评列表 +export function workInboxList(params: object) { + return http.request({ + url: '/work/inbox/list', + method: 'get', + params: params + }) +} + +// 点评详情 +export function workInboxInfo(inboxId: string) { + return http.request({ + url: `/work/inbox/info/${inboxId}`, + method: 'get' + }) +} + +/** 会议 **/ +// 会议列表 +export function workMeetingList(query: string) { + return http.request({ + url: `/work/meeting/list?${query}`, + method: 'get' + }) +} + +export function workMeetingUpdateOrSave(type: string, data: object) { + return http.request({ + url: `/work/meeting/${type ? 'update' : 'save'}`, + method: type ? 'put' : 'post', + data: data + }) +} + +// 会议详情 +export function workMeetingInfo(meetingId: string) { + return http.request({ + url: `/work/meeting/info/${meetingId}`, + method: 'get' + }) +} + +// 删除会议 +export function workMeetingDelete(meetingId: string) { + return http.request( + { + url: '/work/meeting/delete', + method: 'delete', + data: [meetingId] + }, + true + ) +} + +// 会议可见人员 +export function workMeetingUserList(params: object) { + return http.request({ + url: '/work/meeting/user/list', + method: 'get', + params: params + }) +} + +// 通过会议id获取会议资料 +export function workMeetingGetFileId(params: object) { + return http.request({ + url: '/work/meeting/getFileId', + method: 'get', + params: params + }) +} + +// 通过会议id获取参会资料 +export function workMeetinGetUserFileMenuId(params: object) { + return http.request({ + url: '/work/meeting/getUserFileMenuId', + method: 'get', + params: params + }) +} + +// 反查会议 +export function workMeetingLookUp(params: object) { + return http.request({ + url: '/work/meeting/lookup', + method: 'get', + params: params + }) +} + +// 参会人员 +export function cusUserMeetingLogList(params: object) { + return http.request({ + url: '/cus/usermeetinglog/list', + method: 'get', + params: params + }) +} + +// 删除参会人员 +export function cusUserMeetingLogDelete(data: object) { + return http.request( + { + url: '/cus/usermeetinglog/delete', + method: 'delete', + data: data + }, + true + ) +} + +// 修改或新增参会人员 +export function cusUserMeetingLogUpdateOrSave(type: string, data: object) { + return http.request( + { + url: `/cus/usermeetinglog/${type ? 'update' : 'save'}`, + method: type ? 'put' : 'post', + data: type ? data : [data] + }, + !!type + ) +} + +// 获取文件列表 +export function workFileListByMenuId(params: object) { + return http.request({ + url: '/work/file/listByMenuId', + method: 'get', + params: params + }) +} + +/** 报告 **/ +// 报告列表 +export function stockReportList(query: string, params: object) { + return http.request({ + url: `/stock/report/list${query}`, + method: 'get', + params: params + }) +} + +/** 沟通 **/ +// 沟通列表 +export async function cusLinkList(query: string) { + const { data } = await http.request({ + url: `/cus/link/list?${query}`, + method: 'get' + }) + return data.list.map((ele: any) => { + ele.sysDay = ele.sysDate.slice(0, 10) + switch (ele.linkType) { + case '32': + ele.tagColor = 'danger-background-color' + break + case '34': + ele.tagColor = 'primary-background-color' + break + case '31': + ele.tagColor = 'success-background-color' + break + } + return ele + }) +} + +// 沟通详情 +export function cusLinkInfo(linkId: string) { + return http.request({ + url: `/cus/link/info/${linkId}`, + method: 'get' + }) +} + +// 删除沟通 +export function cusLinkDeleteByOne(params: object) { + return http.request({ + url: '/cus/link/deleteByOne', + method: 'delete', + params: params + }) +} + +// 修改新增沟通 +export function cusLinkUpdateOrSave(type: string, data: object) { + return http.request({ + url: `/cus/link/${type ? 'update' : 'save'}`, + method: type ? 'put' : 'post', + data: type ? data : { cusLinkEntityList: [data] } + }) +} + +// 反查机构 +export function cusInfoLookUp(params: object) { + return http.request({ + url: '/cus/info/lookup', + method: 'get', + params: params + }) +} + +// 新增机构 +export function cusInfoSave(data: object) { + return http.request({ + url: '/cus/info/save', + method: 'post', + data: data + }) +} +// 机构列表 +export function cusInfoList(params: object) { + return http.request({ + url: 'cus/info/list', + method: 'get', + params + }) +} +export function cusInfoInfo(id: number) { + return http.request({ + url: `cus/info/info/${id}`, + method: 'get' + }) +} +export function cusServiceGetByCusId (params: object) { + return http.request({ + url: '/cus/service/getByCusId', + method: 'get', + params + }) +} +export function cusYearList (params: object) { + return http.request({ + url: '/cus/year/list', + method: 'get', + params + }) +} +export function cusAccountList (params: object) { + return http.request({ + url: '/cus/account/list', + method: 'get', + params + }) +} +export function cusMeetingList (params: object) { + return http.request({ + url: '/cus/usermeetinglog/listByCusId', + method: 'get', + params + }) +} +export function cusInfoCountByMonth (params: object) { + return http.request({ + url: '/cus/info/countByMonth', + method: 'get', + params + }) +} + +// 交易详情 +export function stockCusTradeMyTrade () { + return http.request({ + url: '/stock/cus/trade/myTrade', + method: 'get' + }) +} + +// 反查联系人 +export function cusUserLookUp(params: object) { + return http.request({ + url: '/cus/user/lookup', + method: 'get', + params: params + }) +} + +export function cusUserCusPositionList() { + return http.request({ + url: '/cus/user/cusPositionList', + method: 'get' + }) +} + +export function cusUserSave(data: object) { + return http.request({ + url: '/cus/user/save', + method: 'post', + data: data + }) +} + +// 通过机构获取联系人 +export function cusUserListByCusId(params: object) { + return http.request({ + url: '/cus/user/listByCusId', + method: 'get', + params: params + }) +} + +/* 股票 */ +// 股票列表 +export function stockStockinfoEventNews(params: object) { + return http.request({ + url: '/stock/stockinfo/event/news', + method: 'get', + params: params + }) +} + +export function stockStockinfoEventList(params: object) { + return http.request({ + url: '/stock/stockinfo/event/list', + method: 'get', + params: params + }) +} + +// 反查股票 +export function stockStockInfoLookUp(params: object) { + return http.request({ + url: '/stock/stockinfo/lookup', + method: 'get', + params: params + }) +} + +export function reportEconomyMonthDepositVsM2() { + return http.request({ + url: '/report/economy/month/depositVsM2', + method: 'get' + }) +} + +export function reportEconomyMonthMarketHSVsDeposit() { + return http.request({ + url: '/report/economy/month/marketHSVsDeposit', + method: 'get' + }) +} + +export function reportEconomyMonthMarketHSJVsDeposit() { + return http.request({ + url: '/report/economy/month/marketHSJVsDeposit', + method: 'get' + }) +} + +export function reportEconomyMonthListByItem(params: object) { + return http.request({ + url: '/report/economy/month/listByItem', + method: 'get', + params: params + }) +} + +export function stockStockrzrqQueryRzrq() { + return http.request({ + url: '/stock/stockrzrq/queryRzrq', + method: 'get' + }) +} + +export function stockStockrzrqQueryRzToTotalMarket() { + return http.request({ + url: '/stock/stockrzrq/queryRzToTotalMarket', + method: 'get' + }) +} + +export function stockStockrzrqQueryRzjme(params: object) { + return http.request({ + url: 'stock/stockrzrq/queryRzjme', + method: 'get', + params: params + }) +} + +export function reportEconomyMonthStockShares() { + return http.request({ + url: '/report/economy/month/stockShares', + method: 'get' + }) +} + +export function reportEconomyMonthStockMarket() { + return http.request({ + url: '/report/economy/month/stockMarket', + method: 'get' + }) +} + +export function reportEconomyMonthStockAmount() { + return http.request({ + url: '/report/economy/month/stockAmount', + method: 'get' + }) +} + +export function reportEconomyMonthStockTurnoverRate() { + return http.request({ + url: '/report/economy/month/stockTurnoverRate', + method: 'get' + }) +} + +export function reportEconomyMonthStockVolume() { + return http.request({ + url: '/report/economy/month/stockVolume', + method: 'get' + }) +} + +export function reportEconomyMonthNewAccountNum() { + return http.request({ + url: '/report/economy/month/newAccountNum', + method: 'get' + }) +} + +export function stockStockHsList() { + return http.request({ + url: '/stock/stockhs/list', + method: 'get' + }) +} + +export function fredInfoList() { + return http.request({ + url: '/fred/info/list', + method: 'get' + }) +} + +export function fredInfoInfo(id: string) { + return http.request({ + url: `/fred/info/info/${id}`, + method: 'get' + }) +} + +export function fredDetailQueryAaaAndBbb(params: object) { + return http.request({ + url: '/fred/detail/query/AaaAndBbb', + method: 'get', + params: params + }) +} + +export function fredDetailQueryFred(params: object) { + return http.request({ + url: '/fred/detail/queryFred', + method: 'get', + params: params + }) +} + +export const indexInfoSeries = (params: object) => { + return http.request({ + url: '/index/info/series', + method: 'get', + params: params + }) +} + +export const indexInfo = (id: string) => { + return http.request({ + url: `/index/info/${id}`, + method: 'get' + }) +} + +export const indexInfoIndexHistChart = (id: string) => { + return http.request({ + url: `/index/info/indexHistChart/${id}`, + method: 'get' + }) +} + +export function indexFundHisQueryFundHis(params: object) { + return http.request({ + url: '/index/fund/his/queryFundHis', + method: 'get', + params: params + }) +} + +export const indexInfoIndustry1Chart = (id: string) => { + return http.request({ + url: `/index/info/industry1Chart/${id}`, + method: 'get' + }) +} + +export const indexInfoIndustry2Chart = (id: string) => { + return http.request({ + url: `/index/info/industry2Chart/${id}`, + method: 'get' + }) +} + +export const indexInfoStockGetChart = (params: object) => { + return http.request({ + url: '/index/info/stock/getChart', + method: 'get', + params: params + }) +} + +export function indexFundList(params: object) { + return http.request({ + url: '/index/fund/list', + method: 'get', + params: params + }) +} + +export function indexFundGetByEtfCode(params: object) { + return http.request({ + url: '/index/fund/getByEtfCode', + method: 'get', + params: params + }) +} + +export function indexFundHisQueryEtfShareHis(params: object) { + return http.request({ + url: '/index/fund/his/queryEtfShareHis', + method: 'get', + params: params + }) +} + +export function indexFundDetailDayCoinList(params: object) { + return http.request({ + url: '/index/fund/detail/day/coinList', + method: 'get', + params: params + }) +} + +export function indexFundDetailDayList(params: object) { + return http.request({ + url: '/index/fund/detail/day/list', + method: 'get', + params: params + }) +} + +export function indexFundDetailQuarterCircleByExchange(params: object) { + return http.request({ + url: '/index/fund/detail/quarter/circleByExchange', + method: 'get', + params: params + }) +} + +export function indexFundDetailQuarterHisByExchange(params: object) { + return http.request({ + url: '/index/fund/detail/quarter/hisByExchange', + method: 'get', + params: params + }) +} + +export function indexFundDetailQuarterGetChart(params: object) { + return http.request({ + url: '/index/fund/detail/quarter/getChart', + method: 'get', + params: params + }) +} + +export function stockAFundList(params: object) { + return http.request({ + url: '/stock/a/fund/list', + method: 'get', + params: params + }) +} + +export const stockAFundInfo = (id: string) => { + return http.request({ + url: `/stock/a/fund/info/${id}`, + method: 'get' + }) +} + +export function stockAFundHisFundHisChart(params: object) { + return http.request({ + url: '/stock/a/fund/his/fundHis/chart', + method: 'get', + params: params + }) +} + +export function stockAFundFileList(params: object) { + return http.request({ + url: '/stock/a/fund/file/list', + method: 'get', + params: params + }) +} + +export function stockAFundHisFundHisList(params: object) { + return http.request({ + url: '/stock/a/fund/his/fundHis/list', + method: 'get', + params: params + }) +} + +export function stockAFundListFund() { + return http.request({ + url: 'stock/a/fund/listFund', + method: 'get' + }) +} + +export const groupList = (params: string) => { + return http.request({ + url: `/group/list?${params}`, + method: 'get' + }) +} + +export function groupInfo(id: string) { + return http.request({ + url: `/group/info/${id}`, + method: 'get' + }) +} + +export function groupHisChartIncomeRate(params: object) { + return http.request({ + url: '/group/his/chart/incomeRate', + method: 'get', + params: params + }) +} + +export function groupHoldList (params: object) { + return http.request({ + url: '/group/holdList', + method: 'get', + params + }) +} + +export function groupTradeList(params: object) { + return http.request({ + url: '/group/trade/list', + method: 'get', + params: params + }) +} + +export function stockEtfDetailLookup(params: object) { + return http.request({ + url: '/stock/etf/detail/lookup', + method: 'get', + params: params + }) +} + +// 内部会议 +export function workSpeakList(params: object) { + return http.request({ + url: '/work/speak/list', + method: 'get', + params: params + }) +} + + +// 获取订阅信息 +export function getSubscriptionList() { + return http.request({ + url: '/stock/subscription/config/list', + method: 'get' + }) +} + +export function delSubscription(id: number) { + return http.request({ + url: '/stock/subscription/config/delete', + method: 'delete', + params: { + id + } + }) +} + +export function saveSubscription(subscriptionItem: string) { + return http.request({ + url: '/stock/subscription/config/save', + method: 'post', + data: { + subscriptionItem + } + }) +} + +// 重点公司追踪 +export function stockCompanyFlowInfoListCompany() { + return http.request({ + url: '/stock/company/follow/info/listCompany', + method: 'get' + }) +} + +export function stockCompanyFlowInfoList(params: object) { + return http.request({ + url: '/stock/company/follow/info/list', + method: 'get', + params: params + }) +} + +export function stockCompanyFlowInfoInfo(id: number) { + return http.request({ + url: `/stock/company/follow/info/info/${id}`, + method: 'get' + }) +} diff --git a/src/utils/chart.ts b/src/utils/chart.ts new file mode 100644 index 0000000..a931a88 --- /dev/null +++ b/src/utils/chart.ts @@ -0,0 +1,180 @@ + + +export function getXAxis(data: any) { + const xAxis: any = { + type: data.type || 'category', + name: data.name || '', + data: data.data || [], + show: data.showFlag !== false, + axisLabel: { + show: data.showFlag !== false, + showMaxLabel: true, + interval: 'auto', + rotate: data.rotate || 0, + formatter: data.axisLabel || '{value}', + }, + gridIndex: data.gridIndex || 0, + } + if (data.max !== null && data.max !== undefined) xAxis.max = data.max + if (data.min !== null && data.min !== undefined) xAxis.min = data.min + if (data.maxInterval !== undefined) xAxis.maxInterval = data.maxInterval + if (data.minInterval !== undefined) xAxis.minInterval = data.minInterval + return xAxis +} + +export function getYAxis(data: any) { + const yAxis: any = { + type: data.type || 'value', + name: data.name || '', + nameLocation: data.nameLocation || 'end', + scale: true, + show: data.showFlag !== false, + axisLabel: { + show: data.showFlag !== false, + formatter: data.axisLabel || '{value}', + }, + axisLine: { + show: true, + lineStyle: { + color: data.color || '#000', + }, + }, + splitLine: { + show: false + }, + position: data.position || 'left', + gridIndex: data.gridIndex || 0, + offset: data.offset || 0, + } + if (data.max !== null && data.max !== undefined) yAxis.max = data.max + if (data.min !== null && data.min !== undefined) yAxis.min = data.min + if (data.maxInterval !== undefined) yAxis.maxInterval = data.maxInterval + if (data.minInterval !== undefined) yAxis.minInterval = data.minInterval + if (data.splitNumber !== undefined) yAxis.splitNumber = data.splitNumber + return yAxis +} + +export function getLineSeries(data: any) { + const series = { + type: 'line', + name: data.name || '', + data: data.data || [], + yAxisIndex: data.yAxisIndex || 0, + xAxisIndex: data.xAxisIndex || 0, + connectNulls: data.connectNulls || false, + symbol: data.symbol || 'none', + symbolSize: data.symbolSize || 0, + lineStyle: + data.color || data.lineType + ? { + color: data.color, + type: data.lineType, + } + : undefined, + markPoint: data.markPoint, + areaStyle: data.areaStyle, + } + return series +} + +export function getBarSeries (data: any) { + const serise = { + type: 'bar', + name: data.name || '', + data: data.data || [], + emphasis: { + focus: 'series' + }, + label: { + show: data.labelFlag !== false, + position: data.position || 'top' + }, + yAxisIndex: data.yAxisIndex || 0, + xAxisIndex: data.xAxisIndex || 0, + stack: data.stack || '', + showEmptyData: false + } + return serise +} + + +export function getBaseOption(data: any) { + data.series.map((ele: any) => { + if (ele.type !== 'line' || !ele?.data) return + if (ele.data.length === 1) { + ele.symbolSize = 4 + ele.symbol = 'circle' + } else if (data.xAxis?.[0]?.type === 'category') { + ele.data.forEach((res: any, index: number) => { + if (typeof res !== 'object' || res === null || !('value' in res) || res.value !== null) + return + const prevPoint = ele.data?.[index - 1] + if ( + index > 0 && + prevPoint && + typeof prevPoint === 'object' && + 'value' in prevPoint && + prevPoint.value !== null + ) { + prevPoint.symbolSize = 4 + prevPoint.symbol = 'circle' + } + const nextPoint = ele.data?.[index + 1] + if ( + ele.data && + index < ele.data.length - 1 && + nextPoint && + typeof nextPoint === 'object' && + 'value' in nextPoint && + nextPoint.value !== null + ) { + nextPoint.symbolSize = 4 + nextPoint.symbol = 'circle' + } + }) + } + }) + const option: any = { + series: data.series, + yAxis: Array.isArray(data.yAxis) ? data.yAxis : data.yAxis ? [data.yAxis] : undefined, + xAxis: Array.isArray(data.xAxis) ? data.xAxis : data.xAxis ? [data.xAxis] : undefined, + } + if (data.tooltip) { + option.tooltip = data.tooltip + } + if (data.tooltipFormatter) { + option.tooltip = { + ...option.tooltip, + trigger: 'axis', + formatter: data.tooltipFormatter, + } + } + if (data.visualMap) option.visualMap = data.visualMap + if (data.toolbox) option.toolbox = data.toolbox + if (data.grid) option.grid = data.grid + if (data.legend) option.legend = data.legend + if (data.color) option.color = data.color + if (data.dataZoom) option.dataZoom = data.dataZoom + if (data.title) { + option.title = Array.isArray(data.title) + ? data.title.map(convertTitle) + : [convertTitle(data.title)] + } + return option +} + +function convertTitle(title: any) { + return { + text: title.text, + subtext: title.subtext, + show: title.show ?? true, + backgroundColor: title.backgroundColor || '#5FCFCB', + textStyle: { + color: title.textStyle?.color || '#fff', + fontSize: title.textStyle?.fontSize || '18', + ...title.textStyle, + }, + left: title.left || 'center', + top: title.top || 0, + } +} \ No newline at end of file diff --git a/src/utils/colorList.ts b/src/utils/colorList.ts new file mode 100644 index 0000000..10774c1 --- /dev/null +++ b/src/utils/colorList.ts @@ -0,0 +1,15 @@ +// 公共颜色列表,用于统一各页面的颜色方案 +export const colorList = [ + { color: '#165DFF', bgColor: '#E8F3FF' }, + { color: '#FF7D00', bgColor: '#FFF7E8' }, + { color: '#00B42A', bgColor: '#E8FFEF' }, + { color: '#F53F3F', bgColor: '#FEE8E8' }, + { color: '#A855F7', bgColor: '#F3E8FF' }, + { color: '#EC4899', bgColor: '#FFE8EA' }, + { color: '#0FC6C2', bgColor: '#EBFFFA' }, + { color: '#722ED1', bgColor: '#F9F0FF' }, + { color: '#FFC53D', bgColor: '#FFF9E8' }, + { color: '#36CFC9', bgColor: '#E6FFFB' }, + { color: '#72BCFF', bgColor: '#EDF5FF' }, + { color: '#73D13D', bgColor: '#F0FFF4' } +] \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..5fdecc1 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,241 @@ +export function getWeek(week: any) { + const weeks = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'] + return weeks[week] +} + +export function deepCopy(obj: any) { + const a = JSON.stringify(obj) + const newobj = JSON.parse(a) + return newobj +} + +export function getStockTwoArray(data: any, type: string) { + const list: any = [] + data[0].forEach((ele: any, index: number) => { + let res = { + stockCode1: ele.stockCode, + stockName1: ele.stockName, + value1: `${ele[type]}${type === 'companyNum' ? '家' : ''}`, + stockCode2: '', + stockName2: '', + value2: '', + stockCode3: '', + stockName3: '', + value3: '', + stockCode4: '', + stockName4: '', + value4: '' + } + res = getStockOneArray(res, data, 1, index, type) + res = getStockOneArray(res, data, 2, index, type) + res = getStockOneArray(res, data, 3, index, type) + list.push(res) + }) + return list +} + +export function getStockOneArray(res: any, data: any, len: number, index: number, type: string) { + if (data.length > len && data[len].length > index) { + res[`stockCode${len + 1}`] = data[len][index].stockCode + res[`stockName${len + 1}`] = data[len][index].stockName + res[`value${len + 1}`] = `${data[len][index][type]}${type === 'companyNum' ? '家' : ''}` + } + return res +} + +export function formatMoney(value: any) { + const arr = (value + '').split('-') + const negativeFlag = '' + value = arr.length === 1 ? arr[0] : arr[1] + if (value > 100000000) { + return negativeFlag + Math.round(value / 1000000) / 100 + '亿' + } else if (value > 10000) { + return negativeFlag + Math.round(value / 100) / 100 + '万' + } else { + return negativeFlag + value + } +} + +export function setDecimalPlaces(value: any, fixedNum: number, multiply = 100, divide = 100) { + if (value) { + return (Math.round(value * multiply) / divide).toFixed(fixedNum) + } else { + return '' + } +} + +import moment from 'moment' +export function getEchartTime(dateValue: any) { + let time = '' + switch (dateValue) { + case '1': + time = moment().subtract(10, 'years').format('YYYY-MM-DD') + break + case '2': + time = moment().subtract(1, 'years').format('YYYY-MM-DD') + break + case '3': + time = moment().subtract(6, 'months').format('YYYY-MM-DD') + break + case '4': + time = moment().subtract(3, 'months').format('YYYY-MM-DD') + break + case '5': + time = moment().subtract(1, 'months').format('YYYY-MM-DD') + break + case '6': + time = moment().subtract(1, 'weeks').format('YYYY-MM-DD') + break + } + return time +} + +export function sortArr(arr: any) { + return arr.sort((value1: any, value2: any) => { + return Date.parse(value1) - Date.parse(value2) + }) +} + +export function convertToUrl(obj: any) { + let url = '' + for (const key in obj) { + if (typeof obj[key] === 'number' || obj[key]) { + url += `&${key}=${obj[key]}` + } + } + return url ? url.slice(1) : '' +} + +import userInfoStore from '@/stores/userInfo' +const userInfo = userInfoStore() +export function isAuth(key: string) { + return userInfo.permissions.indexOf(key) !== -1 || false +} + +import * as CryptoJS from 'crypto-js' +export function encrypt(word: any) { + const keyStr = 'firshshanghai!@#' // 判断是否存在ksy,不存在就用定义好的key + const key = CryptoJS.enc.Utf8.parse(keyStr) + const srcs = CryptoJS.enc.Utf8.parse(word) + const encrypted = CryptoJS.AES.encrypt(srcs, key, { + mode: CryptoJS.mode.ECB, + padding: CryptoJS.pad.Pkcs7 + }) + return encrypted.toString() +} + +export function addOrSubtractTime(date: string | Date, value: number, unit: 'year' | 'month') { + const currentDate = typeof date === 'string' ? new Date(date) : date; + const newDate = new Date(currentDate.getTime()) + if (unit === 'year') { + newDate.setFullYear(newDate.getFullYear() + value) + } else if (unit === 'month') { + newDate.setMonth(newDate.getMonth() + value) + } + const year = newDate.getFullYear(); + const month = String(newDate.getMonth() + 1).padStart(2, '0') + const day = String(newDate.getDate()).padStart(2, '0') + return `${year}-${month}-${day}`; +} + +export function treeDataTranslate(data: any, id = 'id', pid = 'parentId') { + const res: any = [] + const temp: any = {} + for (let i = 0; i < data.length; i++) { + temp[data[i][id]] = data[i] + } + for (let k = 0; k < data.length; k++) { + if (temp[data[k][pid]] && data[k][id] !== data[k][pid]) { + if (!temp[data[k][pid]]['children']) { + temp[data[k][pid]]['children'] = [] + } + if (!temp[data[k][pid]]['_level']) { + temp[data[k][pid]]['_level'] = 1 + } + data[k]['_level'] = temp[data[k][pid]]._level + 1 + temp[data[k][pid]]['children'].push(data[k]) + } else { + res.push(data[k]) + } + } + return res +} + +export function formatNumberByUnit (value: any) { + if (value) { + value = value.toString() + const negativeFlag = value.indexOf('-') > -1 + if (value.indexOf('-') > -1) { + value = value.replaceAll('-', '') + } + if (Number(value) >= 100000000) { + return `${negativeFlag ? '-' : ''}${Math.round(Number(value) / 1000000) / 100 + '亿'}` + } else if (Number(value) >= 10000) { + return `${negativeFlag ? '-' : ''}${Math.round(Number(value) / 100) / 100 + '万'}` + } else { + return `${negativeFlag ? '-' : ''}${value}` + } + } else { + return '' + } +} + +export function formatNumber (n: any) { + if (n) { + n = n.toString() + const negativeFlag = n.indexOf('-') > -1 + if (n.indexOf('-') > -1) { + n = n.replaceAll('-', '') + } + const numArr = n.toString().split('.') + const decmial = numArr[1] ? '.' + numArr[1] : '' + const a = numArr[0] + const b = parseInt(a).toString() + const len = a.length + if (len <= 3) { + return negativeFlag ? `-${b + decmial}` : b + decmial + } + const r = len % 3 + const returnNumber = r > 0 + ? b.slice(0, r) + ',' + (b.slice(r, len).match(/\d{3}/g) || []).join(',') + decmial : (b.slice(r, len).match(/\d{3}/g) || []).join(',') + decmial + return negativeFlag ? `-${returnNumber}` : returnNumber + } else { + return '0' + } +} + +export function padZeroAfterDecimal (num: number) { + let numStr = num.toString() + if (numStr.includes('.')) { + const parts = numStr.split('.') + let decimalPart = parts[1] + if (decimalPart.length < 2) { + decimalPart = decimalPart.padEnd(2, '0') + } else { + decimalPart = decimalPart.slice(0, 2) + } + numStr = parts[0] + '.' + decimalPart + } else { + numStr = numStr + '.00' + } + return numStr +} + +export function getHtmlByValue( + val: any, + unit: any, + nullStr = '', + compareVal = 0, + raiseFlag = '+', + data?: any, + color?: any +) { + if (val === null || val === undefined) return nullStr + if (parseFloat(val) === compareVal) { + return `${data || val}${unit}` + } else if (parseFloat(val) > compareVal) { + return `${raiseFlag}${data || val}${unit}` + } else { + return `${data || val}${unit}` + } +} diff --git a/src/utils/mitt.ts b/src/utils/mitt.ts new file mode 100644 index 0000000..8ec0ce4 --- /dev/null +++ b/src/utils/mitt.ts @@ -0,0 +1,11 @@ +import mitt from 'mitt' + +type Events = { + showLoading: string + hiddenLoading: string + noteType: string + setTitle: object +} + +const emitter = mitt() +export default emitter diff --git a/src/utils/request.ts b/src/utils/request.ts new file mode 100644 index 0000000..73caf61 --- /dev/null +++ b/src/utils/request.ts @@ -0,0 +1,92 @@ +import axios, { type AxiosInstance, type AxiosRequestConfig } from 'axios' +import router from '@/router' +import emitter from '@/utils/mitt' +import userInfoStore from '@/stores/userInfo' +class HttpRequest { + getInsideConfig() { + const config = { + baseURL: import.meta.env.VITE_BASE_URL, + timeout: 600000, + withCredentials: false, + headers: { + 'Content-Type': 'application/json; charset=utf-8' + } + } + return config + } + + // 请求拦截 + interceptors(instance: AxiosInstance, url: string | number | undefined) { + instance.interceptors.request.use( + (config: any) => { + const userInfo = userInfoStore() + config.headers.token = userInfo.token + return config + }, + (error: any) => { + return Promise.reject(error) + } + ) + + // 响应拦截 + instance.interceptors.response.use( + (res: any) => { + const { data } = res + console.log('返回数据处理', res) + if (data.code === 0) { + return data + } else if (data.code === 401 || data.code === 404) { + const userInfo = userInfoStore() + userInfo.token = '' + router.push({ + name: 'login', + query: { + // currentRoute存储了当前路由信息的对象 + redirect: router.currentRoute.value.fullPath + } + }) + emitter.emit('hiddenLoading', '') + } else { + return data + } + }, + (error: any) => { + console.log('error==>', error) + if (error.response && (error.response.status === 401)) { + const userInfo = userInfoStore() + userInfo.token = '' + router.push({ + name: 'login', + query: { + // currentRoute存储了当前路由信息的对象 + redirect: router.currentRoute.value.fullPath + } + }) + } + emitter.emit('hiddenLoading', '') + } + ) + } + + request(options: AxiosRequestConfig, openDefaultData = false) { + const instance = axios.create() + options = Object.assign(this.getInsideConfig(), options) + if (!options.url) { + throw new Error('URL is not defined in request options') + } + + this.interceptors(instance, options.url) + if (options.data && openDefaultData) { + options.data = JSON.stringify(options.data) + } + if (options.url.includes('?')) { + options.url = options.url + '&t=' + new Date().getTime() + } else { + options.url = options.url + '?t=' + new Date().getTime() + } + return instance(options) + } +} + +const http = new HttpRequest() +export default http diff --git a/src/utils/types.ts b/src/utils/types.ts new file mode 100644 index 0000000..629b50c --- /dev/null +++ b/src/utils/types.ts @@ -0,0 +1,326 @@ +export interface CommunicateObj { + showPop1: boolean + showPop2: boolean + showPop3: boolean + showPop4: boolean + showPop5: boolean + showPop6: boolean + popTitle: string + type: string + linkId: string + value1: string[] + value3: string[] + value4: string[] + value5: string[] + linkTypeName: string + selectlinkType: string + linkTypeList: any[] + dicNote2: string + dicNote3: string + sysDay: string + selectsysDay: any[] + sysTime: string + selectsysTime: any[] + cusName: string + searchValue: string + selectcus: string + showPop2List: any[] + cusLevelName: string + selectcusLevel: string + linkTitle: string + cusLevelList: any[] + cusUserNames: string + cusUserList: any[] + selectcusUserIds: any[] + staffNames: string + staffList: any[] + selectstaffIds: any[] + saleNames: string + saleList: any[] + selectsaleIds: any[] + linkFromName: string + linkFrom: number + linkFromRadio: string + linkFromField: string + linkFromList: any[] + linkWayName: string + selectlinkWay: string + linkWayList: any[] + linkNote: string + meetingName: string + selectmeeting: string + staffNote: string + cusLinkStockList: any[] + cusLinkStockListForLinkId: any[] + saleScore: number + saleNote: string + [key: string]: any + handleShowPop1: (val: string, title: string) => void + handleShowPop2: (val: string, title: string) => void + handleShowPop3: (val: string, title: string) => void + handleShowPop4: (val: string, title: string, index: number) => void + handleShowPop5: (val: string, title: string) => void + handleShowPop6: (index: number) => void + onConfirm1: ({ selectedOptions }: { selectedOptions?: any[] }) => void + onConfirm2: ({ selectedOptions }: { selectedOptions?: any[] }) => void + onSearchInput: () => any + deleteStock: (index: number) => void + onConfirm3: () => void + onConfirm4: ({ selectedValues }: { selectedValues?: any[] }) => void + changeLinkFrom: () => void + onConfirm5: () => void +} + +export interface CusObj { + showPop1: boolean + showPop2: boolean + showPop3: boolean + showPop4: boolean + popTitle: string + type: string + value1: string[] + value3: string[] + value4: string[] + cusName: string + cusLevelName: string + selectcusLevel: string + cusLevelList: any[] + cusUserName: string + selectcusUser: string + searchValue: string + showPop2List: any[] + companyTypeName: string + selectcompanyType: string + companyTypeList: any[] + cityName: string + selectcity: string + cityList: any[] + address: string + accountStatus: string + accountTime: string + selectaccountTime: any[] + accountTypeNames: string + selectaccountTypeIds: any[] + accountTypeList: any[] + isMoney: string + tradeMoney: string + capitalScaleName: string + selectcapitalScale: string + capitalScaleList: any[] + capitalScaleValue: string + fundNum: string + investOverseasPer: string + investResearchNum: string + investChannelName: string + selectinvestChannel: string + investChannelList: any[] + website: string + companyCreateTime: string + selectcompanyCreateTime: any[] + introduction: string + saleUserNames: string + selectsaleUserIds: any[] + saleUserList: any[] + [key: string]: any + handleShowPop1: (val: string, title: string) => void + handleShowPop2: (val: string, title: string) => void + handleShowPop3: (val: string, title: string) => void + handleShowPop4: (val: string, title: string) => void + onConfirm1: ({ selectedOptions }: { selectedOptions?: any[] }) => void + onConfirm2: ({ selectedOptions }: { selectedOptions?: any[] }) => void + onSearchInput: () => any + onConfirm3: () => void + onConfirm4: ({ selectedValues }: { selectedValues?: any[] }) => void + setAccountStatus: () => any +} + +export interface CusUserObj { + showPop1: boolean + showPop3: boolean + popTitle: string + type: string + value1: string[] + value3: string[] + cusUserName: string + positionName: string + selectposition: string + positionList: any[] + saleUserNames: string + selectsaleUserIds: any[] + saleUserList: any[] + selectcusUser: string + phone: string + mobile: string + email: string + wxName: string + address: string + preference: string + [key: string]: any + handleShowPop1: (val: string, title: string) => void + handleShowPop3: (val: string, title: string) => void + onConfirm1: ({ selectedOptions }: { selectedOptions?: any[] }) => void + onConfirm3: () => void +} + +export interface MeetingObj { + showPop1: boolean + showPop2: boolean + showPop3: boolean + showPop4: boolean + showPop5: boolean + showPop6: boolean + popTitle: string + type: string + value1: string[] + value3: string[] + meetingTitle: string + sysDay: any + selectsysDay: any[] + sysTime: string + selectsysTime: any[] + meetingTypeName: string + selectmeetingType: string + meetingTypeList: any[] + meetingByName: string + selectmeetingBy: string + meetingByList: any[] + stockCodeList: any[] + searchValue: string + showPop2List: any[] + meetingGuestList: any[] + linkFromName: string + linkFrom: number + linkFromRadio: string + linkFromField: string + linkFromList: any[] + phoneBy: string + netBy: string + meetingPassword: string + organizerNames: string + selectorganizerIds: any[] + organizerList: any[] + content: string + visibleName: string + selectvisible: string + visibleList: any[] + staffNames: string + staffList: any[] + selectstaffIds: any[] + [key: string]: any + handleShowPop1: (val: string, title: string) => void + handleShowPop3: (val: string, title: string) => void + handleShowPop4: (val: string, title: string, index: number) => void + handleShowPop5: (val: string, title: string) => void + handleShowPop6: (val: string, title: string, index: number) => void + onSearchInput: () => any + deleteItemByType: (val: string, index: number) => void + onConfirm1: ({ selectedOptions }: { selectedOptions?: any[] }) => void + onConfirm2: ({ selectedOptions }: { selectedOptions?: any[] }) => void + onConfirm3: () => void + onConfirm4: ({ selectedValues }: { selectedValues?: any[] }) => void + changeLinkFrom: () => void + onConfirm5: () => void +} + +export interface GroupObj { + stockGroupName: string + isPublic: string +} + +export interface HisObj { + sysDate: any + selectsysDate: any[] + groupCodeId: any + id: any + dividend24Percent: string + dividend25Percent: string + showPop: boolean + value: string[] + type: string + popTitle: string + handleShowPop: (val: string, title: string) => void + onConfirm: ({ selectedValues }: { selectedValues?: any[] }) => void + [key: string]: any +} + +export interface BuyOrSellObj { + groupId: any + tradeTime: any + selecttradeTime: any[] + isSell: number + num: number + stockName: string + stockCode: string + tradePrice: string + note: string + showPop: boolean + showPop2: boolean + searchValue: string + value: string[] + type: string + popTitle: string + handleShowPop: (val: string, title: string) => void + handleShowPop2: (val: string, title: string) => void + onConfirm: ({ selectedValues }: { selectedValues?: any[] }) => void + onSearchInput: () => any + onConfirm2: ({ selectedOptions }: { selectedOptions?: any[] }) => void + [key: string]: any +} + +export interface bonnusObj { + groupId: any + stockName: string + selectstock: string + stockList: any[] + dividendDate: any + selectdividendDate: any[] + exDividendDate: any + selectexDividendDate: any[] + dps: number + sg1: any + sg2: any + note: string + showPop: boolean + showPop2: boolean + value: string[] + value1: string[] + type: string + popTitle: string + handleShowPop: (val: string, title: string) => void + handleShowPop2: (val: string, title: string) => void + onConfirm: ({ selectedValues }: { selectedValues?: any[] }) => void + onConfirm2: ({ selectedOptions }: { selectedOptions?: any[] }) => void + [key: string]: any +} +export interface IHoldList { + grouId?: number + stockCode?: string | number + stockName?: string + stockPricePercent?: number | string + stockPrice?: number | string + costPrice?: number | string + hkStockMarket?: number | string + holdPercent?: string | number + todayIncome?: number | string + totalIncome?: number | string + holdCost?: number | string + shares?: number | string + dividendMoney?: number | string + priceIncome?: number | string + dividendTaxMoney?: number | string +} +export interface IDicList { + id: number + dicType: string + dicKey: string + dicValue: string + dicNote: string + dicNote2: string + dicNote3: string + dicColor: string + dicSort: string +} +export interface IUserLog { + note: string + userName: string +} \ No newline at end of file diff --git a/src/views/comment/detail.vue b/src/views/comment/detail.vue new file mode 100644 index 0000000..0b0a169 --- /dev/null +++ b/src/views/comment/detail.vue @@ -0,0 +1,90 @@ + + + diff --git a/src/views/comment/list.vue b/src/views/comment/list.vue new file mode 100644 index 0000000..dd24895 --- /dev/null +++ b/src/views/comment/list.vue @@ -0,0 +1,125 @@ + + + + diff --git a/src/views/common/error.vue b/src/views/common/error.vue new file mode 100644 index 0000000..4bbf410 --- /dev/null +++ b/src/views/common/error.vue @@ -0,0 +1,11 @@ + + + diff --git a/src/views/common/loading-page.vue b/src/views/common/loading-page.vue new file mode 100644 index 0000000..3950f8b --- /dev/null +++ b/src/views/common/loading-page.vue @@ -0,0 +1,195 @@ + + + \ No newline at end of file diff --git a/src/views/common/log.vue b/src/views/common/log.vue new file mode 100644 index 0000000..ed3074d --- /dev/null +++ b/src/views/common/log.vue @@ -0,0 +1,112 @@ + + + + \ No newline at end of file diff --git a/src/views/common/login.vue b/src/views/common/login.vue new file mode 100644 index 0000000..c80cb06 --- /dev/null +++ b/src/views/common/login.vue @@ -0,0 +1,182 @@ + + + \ No newline at end of file diff --git a/src/views/common/subscription.vue b/src/views/common/subscription.vue new file mode 100644 index 0000000..7ab0d1a --- /dev/null +++ b/src/views/common/subscription.vue @@ -0,0 +1,53 @@ + + + + \ No newline at end of file diff --git a/src/views/communicate/add-cus.vue b/src/views/communicate/add-cus.vue new file mode 100644 index 0000000..1025a3d --- /dev/null +++ b/src/views/communicate/add-cus.vue @@ -0,0 +1,449 @@ + + + + + diff --git a/src/views/communicate/add-cusUser.vue b/src/views/communicate/add-cusUser.vue new file mode 100644 index 0000000..ae5d9d5 --- /dev/null +++ b/src/views/communicate/add-cusUser.vue @@ -0,0 +1,222 @@ + + + + + diff --git a/src/views/communicate/detail.vue b/src/views/communicate/detail.vue new file mode 100644 index 0000000..c43bbd1 --- /dev/null +++ b/src/views/communicate/detail.vue @@ -0,0 +1,1038 @@ + + + + + diff --git a/src/views/communicate/list.vue b/src/views/communicate/list.vue new file mode 100644 index 0000000..d0fd99c --- /dev/null +++ b/src/views/communicate/list.vue @@ -0,0 +1,254 @@ + + + + + diff --git a/src/views/flow/company.vue b/src/views/flow/company.vue new file mode 100644 index 0000000..d77841e --- /dev/null +++ b/src/views/flow/company.vue @@ -0,0 +1,90 @@ + + + + diff --git a/src/views/flow/flow-detail.vue b/src/views/flow/flow-detail.vue new file mode 100644 index 0000000..43fd39b --- /dev/null +++ b/src/views/flow/flow-detail.vue @@ -0,0 +1,47 @@ + + + + diff --git a/src/views/flow/flow.vue b/src/views/flow/flow.vue new file mode 100644 index 0000000..ba35225 --- /dev/null +++ b/src/views/flow/flow.vue @@ -0,0 +1,60 @@ + + + + diff --git a/src/views/home/index.vue b/src/views/home/index.vue new file mode 100644 index 0000000..0f3aedd --- /dev/null +++ b/src/views/home/index.vue @@ -0,0 +1,202 @@ + + + + \ No newline at end of file diff --git a/src/views/internal/meeting.vue b/src/views/internal/meeting.vue new file mode 100644 index 0000000..6885287 --- /dev/null +++ b/src/views/internal/meeting.vue @@ -0,0 +1,135 @@ + + + + \ No newline at end of file diff --git a/src/views/meeting/add-or-update-meeting.vue b/src/views/meeting/add-or-update-meeting.vue new file mode 100644 index 0000000..9613245 --- /dev/null +++ b/src/views/meeting/add-or-update-meeting.vue @@ -0,0 +1,683 @@ + + + + + diff --git a/src/views/meeting/detail.vue b/src/views/meeting/detail.vue new file mode 100644 index 0000000..0c7a4e2 --- /dev/null +++ b/src/views/meeting/detail.vue @@ -0,0 +1,436 @@ + + + + diff --git a/src/views/meeting/list.vue b/src/views/meeting/list.vue new file mode 100644 index 0000000..363d3df --- /dev/null +++ b/src/views/meeting/list.vue @@ -0,0 +1,237 @@ + + + + diff --git a/src/views/organization/detail.vue b/src/views/organization/detail.vue new file mode 100644 index 0000000..b0f74b8 --- /dev/null +++ b/src/views/organization/detail.vue @@ -0,0 +1,487 @@ + + + + \ No newline at end of file diff --git a/src/views/organization/list.vue b/src/views/organization/list.vue new file mode 100644 index 0000000..549b988 --- /dev/null +++ b/src/views/organization/list.vue @@ -0,0 +1,112 @@ + + + + diff --git a/src/views/report/list.vue b/src/views/report/list.vue new file mode 100644 index 0000000..ab132a8 --- /dev/null +++ b/src/views/report/list.vue @@ -0,0 +1,267 @@ + + + + diff --git a/src/views/stock/detail.vue b/src/views/stock/detail.vue new file mode 100644 index 0000000..982e54f --- /dev/null +++ b/src/views/stock/detail.vue @@ -0,0 +1,189 @@ + + + + diff --git a/src/views/stock/list.vue b/src/views/stock/list.vue new file mode 100644 index 0000000..249dedc --- /dev/null +++ b/src/views/stock/list.vue @@ -0,0 +1,121 @@ + + + + diff --git a/src/views/target/economy/economy-detail.vue b/src/views/target/economy/economy-detail.vue new file mode 100644 index 0000000..326f3e1 --- /dev/null +++ b/src/views/target/economy/economy-detail.vue @@ -0,0 +1,770 @@ + + + + diff --git a/src/views/target/economy/economy-list.vue b/src/views/target/economy/economy-list.vue new file mode 100644 index 0000000..4e67077 --- /dev/null +++ b/src/views/target/economy/economy-list.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/src/views/target/etf/etf-detail.vue b/src/views/target/etf/etf-detail.vue new file mode 100644 index 0000000..146cc12 --- /dev/null +++ b/src/views/target/etf/etf-detail.vue @@ -0,0 +1,663 @@ + + + + diff --git a/src/views/target/fred/fred-detail.vue b/src/views/target/fred/fred-detail.vue new file mode 100644 index 0000000..6a1bcc4 --- /dev/null +++ b/src/views/target/fred/fred-detail.vue @@ -0,0 +1,362 @@ + + + + diff --git a/src/views/target/fred/fred-list.vue b/src/views/target/fred/fred-list.vue new file mode 100644 index 0000000..c8d90bc --- /dev/null +++ b/src/views/target/fred/fred-list.vue @@ -0,0 +1,58 @@ + + + + + diff --git a/src/views/target/fund/fund-detail.vue b/src/views/target/fund/fund-detail.vue new file mode 100644 index 0000000..ec0ddde --- /dev/null +++ b/src/views/target/fund/fund-detail.vue @@ -0,0 +1,769 @@ + + + + diff --git a/src/views/target/fund/fund-list.vue b/src/views/target/fund/fund-list.vue new file mode 100644 index 0000000..7908071 --- /dev/null +++ b/src/views/target/fund/fund-list.vue @@ -0,0 +1,260 @@ + + + + + diff --git a/src/views/target/group/group-detail.vue b/src/views/target/group/group-detail.vue new file mode 100644 index 0000000..0a96e59 --- /dev/null +++ b/src/views/target/group/group-detail.vue @@ -0,0 +1,356 @@ + + + + diff --git a/src/views/target/group/group-list.vue b/src/views/target/group/group-list.vue new file mode 100644 index 0000000..43a6c2b --- /dev/null +++ b/src/views/target/group/group-list.vue @@ -0,0 +1,213 @@ + + + + + diff --git a/src/views/target/index/index-detail.vue b/src/views/target/index/index-detail.vue new file mode 100644 index 0000000..2ba3d17 --- /dev/null +++ b/src/views/target/index/index-detail.vue @@ -0,0 +1,473 @@ + + + + diff --git a/src/views/target/index/index-list.vue b/src/views/target/index/index-list.vue new file mode 100644 index 0000000..95d396a --- /dev/null +++ b/src/views/target/index/index-list.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/src/views/target/series/series-list.vue b/src/views/target/series/series-list.vue new file mode 100644 index 0000000..f592699 --- /dev/null +++ b/src/views/target/series/series-list.vue @@ -0,0 +1,53 @@ + + + + + diff --git a/src/views/target/table.vue b/src/views/target/table.vue new file mode 100644 index 0000000..e8a4e6c --- /dev/null +++ b/src/views/target/table.vue @@ -0,0 +1,122 @@ + + + + diff --git a/src/views/user/index.vue b/src/views/user/index.vue new file mode 100644 index 0000000..d8c06ea --- /dev/null +++ b/src/views/user/index.vue @@ -0,0 +1,128 @@ + + + + \ No newline at end of file diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..e14c754 --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,14 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], + "exclude": ["src/**/__tests__/*"], + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..b17be69 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "ignoreDeprecations": "5.0" + }, + "files": [], + "references": [ + { + "path": "./tsconfig.node.json" + }, + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.vitest.json" + } + ] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..f094063 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,19 @@ +{ + "extends": "@tsconfig/node20/tsconfig.json", + "include": [ + "vite.config.*", + "vitest.config.*", + "cypress.config.*", + "nightwatch.conf.*", + "playwright.config.*" + ], + "compilerOptions": { + "composite": true, + "noEmit": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + + "module": "ESNext", + "moduleResolution": "Bundler", + "types": ["node"] + } +} diff --git a/tsconfig.vitest.json b/tsconfig.vitest.json new file mode 100644 index 0000000..571995d --- /dev/null +++ b/tsconfig.vitest.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.app.json", + "exclude": [], + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo", + + "lib": [], + "types": ["node", "jsdom"] + } +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..432a60f --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,117 @@ +import { fileURLToPath, URL } from 'node:url' +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import AutoImport from 'unplugin-auto-import/vite' +import Components from 'unplugin-vue-components/vite' +import { VantResolver } from '@vant/auto-import-resolver' +import externalGlobals from 'rollup-plugin-external-globals' +import { createHtmlPlugin } from 'vite-plugin-html' +// 文件压缩 +import viteCompression from 'vite-plugin-compression' +function getDir() { + const d = new Date() + const yy = d.getFullYear() + const MM = d.getMonth() + 1 >= 10 ? d.getMonth() + 1 + '' : '0' + (d.getMonth() + 1) + const DD = d.getDate() >= 10 ? d.getDate() + '' : '0' + d.getDate() + const h = d.getHours() >= 10 ? d.getHours() + '' : '0' + d.getHours() + const mm = d.getMinutes() >= 10 ? d.getMinutes() + '' : '0' + d.getMinutes() + const version = yy + MM + DD + h + mm + return version + '/static' +} +const dir = getDir() + +const cdn = { + css: ['https://unpkg.com/vue3-pdf-app@1.0.3/dist/icons/main.css'], + js: [ + 'https://unpkg.com/vue@3.4.21/dist/vue.global.js', + 'https://unpkg.com/vue-demi@0.14.8', + 'https://unpkg.com/pinia@2.1.7', + 'https://unpkg.com/vue-router@4.3.0', + 'https://unpkg.com/vant@4.9.1', + 'https://unpkg.com/axios@1.7.2/dist/axios.min.js', + 'https://unpkg.com/echarts@5.5.1/dist/echarts.js', + 'https://unpkg.com/moment@2.30.1/moment.js' + ] +} + +const externalGlobalsObj = { + vue: 'Vue', + 'vue-demi': 'VueDemi', + pinia: 'Pinia', + 'vue-router': 'VueRouter', + vant: 'Vant', + axios: 'axios', + echarts: 'echarts', + moment: 'moment' +} + +export default defineConfig(({ mode }) => { + const isProduction = mode === 'production' || mode === 'test' + return { + plugins: [ + vue(), + AutoImport({ + resolvers: [VantResolver()] + }), + Components({ + resolvers: [VantResolver()] + }), + createHtmlPlugin({ + inject: { + data: { + cdnCss: isProduction ? cdn.css : [], + cdnJs: isProduction ? cdn.js : [] + } + } + }), + //在plugins配置数组里添加gzip插件 + viteCompression({ + verbose: true, + disable: false, + threshold: 10240, + algorithm: 'gzip', + ext: '.gz' + }), + { + ...externalGlobals(externalGlobalsObj), + enforce: 'post', + apply: 'build' + } + ], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), + '/img': fileURLToPath(new URL('./src/assets/img', import.meta.url)) + }, + extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'] + }, + build: { + target: 'es2015', + assetsDir: dir, + minify: 'terser', // 必须开启:使用terserOptions才有效果 + terserOptions: { + compress: { + keep_infinity: true, // 防止 Infinity 被压缩成 1/0,这可能会导致 Chrome 上的性能问题 + drop_console: false, // 生产环境去除 console + drop_debugger: true // 生产环境去除 debugger + } + }, + chunkSizeWarningLimit: 2000, // chunk 大小警告的限制(以 kbs 为单位) + rollupOptions: { + external: Object.keys(externalGlobalsObj), + output: { + chunkFileNames: `${dir}/js/chunk/[name]-[hash].js`, + entryFileNames: `${dir}/js/entry/[name]-[hash].js`, + assetFileNames: `${dir}/[ext]/[name]-[hash].[ext]` + } + } + }, + css: { + preprocessorOptions: { + scss: { + additionalData: `` + } + } + } + } +}) diff --git a/vite.config.ts.timestamp-1719196917102-b76a7f7c00d2a.mjs b/vite.config.ts.timestamp-1719196917102-b76a7f7c00d2a.mjs new file mode 100644 index 0000000..a036ddf --- /dev/null +++ b/vite.config.ts.timestamp-1719196917102-b76a7f7c00d2a.mjs @@ -0,0 +1,119 @@ +// vite.config.ts +import { fileURLToPath, URL } from 'node:url' +import { defineConfig } from 'file:///E:/code/vue3-h5-stock/node_modules/vite/dist/node/index.js' +import vue from 'file:///E:/code/vue3-h5-stock/node_modules/@vitejs/plugin-vue/dist/index.mjs' +import AutoImport from 'file:///E:/code/vue3-h5-stock/node_modules/unplugin-auto-import/dist/vite.js' +import Components from 'file:///E:/code/vue3-h5-stock/node_modules/unplugin-vue-components/dist/vite.js' +import { VantResolver } from 'file:///E:/code/vue3-h5-stock/node_modules/@vant/auto-import-resolver/dist/index.esm.mjs' +import externalGlobals from 'file:///E:/code/vue3-h5-stock/node_modules/rollup-plugin-external-globals/index.js' +import { createHtmlPlugin } from 'file:///E:/code/vue3-h5-stock/node_modules/vite-plugin-html/dist/index.mjs' +import viteCompression from 'file:///E:/code/vue3-h5-stock/node_modules/vite-plugin-compression/dist/index.mjs' +var __vite_injected_original_import_meta_url = 'file:///E:/code/vue3-h5-stock/vite.config.ts' +function getDir() { + const d = /* @__PURE__ */ new Date() + const yy = d.getFullYear() + const MM = d.getMonth() + 1 >= 10 ? d.getMonth() + 1 + '' : '0' + (d.getMonth() + 1) + const DD = d.getDate() >= 10 ? d.getDate() + '' : '0' + d.getDate() + const h = d.getHours() >= 10 ? d.getHours() + '' : '0' + d.getHours() + const mm = d.getMinutes() >= 10 ? d.getMinutes() + '' : '0' + d.getMinutes() + const version = yy + MM + DD + h + mm + return version + '/static' +} +var dir = getDir() +var cdn = { + css: ['https://unpkg.com/vue3-pdf-app@1.0.3/dist/icons/main.css'], + js: [ + 'https://unpkg.com/vue@3.4.21/dist/vue.global.js', + 'https://unpkg.com/pinia@2.1.7', + 'https://unpkg.com/vue-router@4.3.0', + 'https://unpkg.com/vant@4.9.1' + ] +} +var externalGlobalsObj = { + vue: 'Vue', + pinia: 'Pinia', + 'vue-router': 'VueRouter', + vant: 'Vant' +} +var vite_config_default = defineConfig(({ mode }) => { + console.log(mode) + const isProduction = mode === 'production' + return { + plugins: [ + vue(), + AutoImport({ + resolvers: [VantResolver()] + }), + Components({ + resolvers: [VantResolver()] + }), + createHtmlPlugin({ + inject: { + data: { + cdnCss: isProduction ? cdn.css : [], + cdnJs: isProduction ? cdn.js : [] + } + } + }), + //在plugins配置数组里添加gzip插件 + viteCompression({ + verbose: true, + disable: false, + threshold: 10240, + algorithm: 'gzip', + ext: '.gz' + }), + { + ...externalGlobals(externalGlobalsObj), + enforce: 'post', + apply: 'build' + } + ], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', __vite_injected_original_import_meta_url)), + '/img': fileURLToPath(new URL('./src/assets/img', __vite_injected_original_import_meta_url)) + }, + extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'] + }, + build: { + target: 'es2015', + assetsDir: dir, + minify: 'terser', + // 必须开启:使用terserOptions才有效果 + terserOptions: { + compress: { + keep_infinity: true, + // 防止 Infinity 被压缩成 1/0,这可能会导致 Chrome 上的性能问题 + drop_console: true, + // 生产环境去除 console + drop_debugger: true + // 生产环境去除 debugger + } + }, + chunkSizeWarningLimit: 2e3, + // chunk 大小警告的限制(以 kbs 为单位) + rollupOptions: { + external: Object.keys(externalGlobalsObj), + output: { + chunkFileNames: `${dir}/js/chunk/[name]-[hash].js`, + entryFileNames: `${dir}/js/entry/[name]-[hash].js`, + assetFileNames: `${dir}/[ext]/[name]-[hash].[ext]` + } + } + } + /* css: { + preprocessorOptions: { + scss: { + // ElementPlus自动导入定制化样式文件进行样式覆盖 @use "@/assets/styles/element.scss" as *; + // 全局样式变量 @use "@/assets/styles/var.scss" as *;; + additionalData: ` + @use "@/assets/styles/var.scss" as *; + `, + } + } + } */ + } +}) +export { vite_config_default as default } +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJFOlxcXFxjb2RlXFxcXHZ1ZTMtaDUtc3RvY2tcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIkU6XFxcXGNvZGVcXFxcdnVlMy1oNS1zdG9ja1xcXFx2aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vRTovY29kZS92dWUzLWg1LXN0b2NrL3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZmlsZVVSTFRvUGF0aCwgVVJMIH0gZnJvbSAnbm9kZTp1cmwnXHJcbmltcG9ydCB7IGRlZmluZUNvbmZpZyB9IGZyb20gJ3ZpdGUnXHJcbmltcG9ydCB2dWUgZnJvbSAnQHZpdGVqcy9wbHVnaW4tdnVlJ1xyXG5pbXBvcnQgQXV0b0ltcG9ydCBmcm9tICd1bnBsdWdpbi1hdXRvLWltcG9ydC92aXRlJ1xyXG5pbXBvcnQgQ29tcG9uZW50cyBmcm9tICd1bnBsdWdpbi12dWUtY29tcG9uZW50cy92aXRlJ1xyXG5pbXBvcnQgeyBWYW50UmVzb2x2ZXIgfSBmcm9tICdAdmFudC9hdXRvLWltcG9ydC1yZXNvbHZlcidcclxuaW1wb3J0IGV4dGVybmFsR2xvYmFscyBmcm9tICdyb2xsdXAtcGx1Z2luLWV4dGVybmFsLWdsb2JhbHMnXHJcbmltcG9ydCB7IGNyZWF0ZUh0bWxQbHVnaW4gfSBmcm9tICd2aXRlLXBsdWdpbi1odG1sJ1xyXG4vLyBcdTY1ODdcdTRFRjZcdTUzOEJcdTdGMjlcclxuaW1wb3J0IHZpdGVDb21wcmVzc2lvbiBmcm9tICd2aXRlLXBsdWdpbi1jb21wcmVzc2lvbidcclxuZnVuY3Rpb24gZ2V0RGlyKCkge1xyXG4gIGNvbnN0IGQgPSBuZXcgRGF0ZSgpXHJcbiAgY29uc3QgeXkgPSBkLmdldEZ1bGxZZWFyKClcclxuICBjb25zdCBNTSA9IGQuZ2V0TW9udGgoKSArIDEgPj0gMTAgPyBkLmdldE1vbnRoKCkgKyAxICsgJycgOiAnMCcgKyAoZC5nZXRNb250aCgpICsgMSlcclxuICBjb25zdCBERCA9IGQuZ2V0RGF0ZSgpID49IDEwID8gZC5nZXREYXRlKCkgKyAnJyA6ICcwJyArIGQuZ2V0RGF0ZSgpXHJcbiAgY29uc3QgaCA9IGQuZ2V0SG91cnMoKSA+PSAxMCA/IGQuZ2V0SG91cnMoKSArICcnIDogJzAnICsgZC5nZXRIb3VycygpXHJcbiAgY29uc3QgbW0gPSBkLmdldE1pbnV0ZXMoKSA+PSAxMCA/IGQuZ2V0TWludXRlcygpICsgJycgOiAnMCcgKyBkLmdldE1pbnV0ZXMoKVxyXG4gIGNvbnN0IHZlcnNpb24gPSB5eSArIE1NICsgREQgKyBoICsgbW1cclxuICByZXR1cm4gdmVyc2lvbiArICcvc3RhdGljJ1xyXG59XHJcbmNvbnN0IGRpciA9IGdldERpcigpXHJcblxyXG5jb25zdCBjZG4gPSB7XHJcbiAgY3NzOiBbJ2h0dHBzOi8vdW5wa2cuY29tL3Z1ZTMtcGRmLWFwcEAxLjAuMy9kaXN0L2ljb25zL21haW4uY3NzJ10sXHJcbiAganM6IFtcclxuICAgICdodHRwczovL3VucGtnLmNvbS92dWVAMy40LjIxL2Rpc3QvdnVlLmdsb2JhbC5qcycsXHJcbiAgICAnaHR0cHM6Ly91bnBrZy5jb20vcGluaWFAMi4xLjcnLFxyXG4gICAgJ2h0dHBzOi8vdW5wa2cuY29tL3BpbmlhLXBsdWdpbi1wZXJzaXN0ZWRzdGF0ZUAzLjIuMScsXHJcbiAgICAnaHR0cHM6Ly91bnBrZy5jb20vdnVlLXJvdXRlckA0LjMuMCcsXHJcbiAgICAnaHR0cHM6Ly91bnBrZy5jb20vYXhpb3NAMS43LjInLFxyXG4gICAgJ2h0dHBzOi8vdW5wa2cuY29tL3ZhbnRANC45LjEnLFxyXG4gICAgJ2h0dHBzOi8vdW5wa2cuY29tL21pdHRAMy4wLjEnLFxyXG4gICAgJ2h0dHBzOi8vdW5wa2cuY29tL21vbWVudEAyLjMwLjEnLFxyXG4gICAgJ2h0dHBzOi8vdW5wa2cuY29tL2NyeXB0by1qc0A0LjIuMCcsXHJcbiAgICAnaHR0cHM6Ly91bnBrZy5jb20veGxzeEAwLjE4LjUnLFxyXG4gICAgJ2h0dHBzOi8vdW5wa2cuY29tL3Z1ZTMtcGRmLWFwcEAxLjAuMydcclxuICBdXHJcbn1cclxuXHJcbmNvbnN0IGV4dGVybmFsR2xvYmFsc09iaiA9IHtcclxuICB2dWU6ICdWdWUnLFxyXG4gIHBpbmlhOiAnUGluaWEnLFxyXG4gICdwaW5pYS1wbHVnaW4tcGVyc2lzdGVkc3RhdGUnOiAnUGluaWFQbHVnaW5QZXJzaXN0ZWRzdGF0ZScsXHJcbiAgJ3Z1ZS1yb3V0ZXInOiAnVnVlUm91dGVyJyxcclxuICBheGlvczogJ0F4aW9zJyxcclxuICB2YW50OiAnVmFudCcsXHJcbiAgbWl0dDogJ01pdHQnLFxyXG4gIG1vbWVudDogJ01vbWVudCcsXHJcbiAgJ2NyeXB0by1qcyc6ICdDcnlwdG8tSnMnLFxyXG4gIHhsc3g6ICdYbHN4JyxcclxuICAndnVlMy1wZGYtYXBwJzogJ1Z1ZTMtUGRmLUFwcCdcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKCh7IG1vZGUgfSkgPT4ge1xyXG4gIGNvbnNvbGUubG9nKG1vZGUpXHJcbiAgY29uc3QgaXNQcm9kdWN0aW9uID0gbW9kZSA9PT0gJ3Byb2R1Y3Rpb24nXHJcbiAgcmV0dXJuIHtcclxuICAgIHBsdWdpbnM6IFtcclxuICAgICAgdnVlKCksXHJcbiAgICAgIEF1dG9JbXBvcnQoe1xyXG4gICAgICAgIHJlc29sdmVyczogW1ZhbnRSZXNvbHZlcigpXVxyXG4gICAgICB9KSxcclxuICAgICAgQ29tcG9uZW50cyh7XHJcbiAgICAgICAgcmVzb2x2ZXJzOiBbVmFudFJlc29sdmVyKCldXHJcbiAgICAgIH0pLFxyXG4gICAgICBjcmVhdGVIdG1sUGx1Z2luKHtcclxuICAgICAgICBpbmplY3Q6IHtcclxuICAgICAgICAgIGRhdGE6IHtcclxuICAgICAgICAgICAgY2RuQ3NzOiBpc1Byb2R1Y3Rpb24gPyBjZG4uY3NzIDogW10sXHJcbiAgICAgICAgICAgIGNkbkpzOiBpc1Byb2R1Y3Rpb24gPyBjZG4uanMgOiBbXVxyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfSksXHJcbiAgICAgIC8vXHU1NzI4cGx1Z2luc1x1OTE0RFx1N0Y2RVx1NjU3MFx1N0VDNFx1OTFDQ1x1NkRGQlx1NTJBMGd6aXBcdTYzRDJcdTRFRjZcclxuICAgICAgdml0ZUNvbXByZXNzaW9uKHtcclxuICAgICAgICB2ZXJib3NlOiB0cnVlLFxyXG4gICAgICAgIGRpc2FibGU6IGZhbHNlLFxyXG4gICAgICAgIHRocmVzaG9sZDogMTAyNDAsXHJcbiAgICAgICAgYWxnb3JpdGhtOiAnZ3ppcCcsXHJcbiAgICAgICAgZXh0OiAnLmd6J1xyXG4gICAgICB9KSxcclxuICAgICAge1xyXG4gICAgICAgIC4uLmV4dGVybmFsR2xvYmFscyhleHRlcm5hbEdsb2JhbHNPYmopLFxyXG4gICAgICAgIGVuZm9yY2U6ICdwb3N0JyxcclxuICAgICAgICBhcHBseTogJ2J1aWxkJ1xyXG4gICAgICB9XHJcbiAgICBdLFxyXG4gICAgcmVzb2x2ZToge1xyXG4gICAgICBhbGlhczoge1xyXG4gICAgICAgICdAJzogZmlsZVVSTFRvUGF0aChuZXcgVVJMKCcuL3NyYycsIGltcG9ydC5tZXRhLnVybCkpLFxyXG4gICAgICAgICcvaW1nJzogZmlsZVVSTFRvUGF0aChuZXcgVVJMKCcuL3NyYy9hc3NldHMvaW1nJywgaW1wb3J0Lm1ldGEudXJsKSlcclxuICAgICAgfSxcclxuICAgICAgZXh0ZW5zaW9uczogWycubWpzJywgJy5qcycsICcudHMnLCAnLmpzeCcsICcudHN4JywgJy5qc29uJywgJy52dWUnXVxyXG4gICAgfSxcclxuICAgIGJ1aWxkOiB7XHJcbiAgICAgIHRhcmdldDogJ2VzMjAxNScsXHJcbiAgICAgIGFzc2V0c0RpcjogZGlyLFxyXG4gICAgICBtaW5pZnk6ICd0ZXJzZXInLCAvLyBcdTVGQzVcdTk4N0JcdTVGMDBcdTU0MkZcdUZGMUFcdTRGN0ZcdTc1Mjh0ZXJzZXJPcHRpb25zXHU2MjREXHU2NzA5XHU2NTQ4XHU2NzlDXHJcbiAgICAgIHRlcnNlck9wdGlvbnM6IHtcclxuICAgICAgICBjb21wcmVzczoge1xyXG4gICAgICAgICAga2VlcF9pbmZpbml0eTogdHJ1ZSwgLy8gXHU5NjMyXHU2QjYyIEluZmluaXR5IFx1ODhBQlx1NTM4Qlx1N0YyOVx1NjIxMCAxLzBcdUZGMENcdThGRDlcdTUzRUZcdTgwRkRcdTRGMUFcdTVCRkNcdTgxRjQgQ2hyb21lIFx1NEUwQVx1NzY4NFx1NjAyN1x1ODBGRFx1OTVFRVx1OTg5OFxyXG4gICAgICAgICAgZHJvcF9jb25zb2xlOiB0cnVlLCAvLyBcdTc1MUZcdTRFQTdcdTczQUZcdTU4ODNcdTUzQkJcdTk2NjQgY29uc29sZVxyXG4gICAgICAgICAgZHJvcF9kZWJ1Z2dlcjogdHJ1ZSAvLyBcdTc1MUZcdTRFQTdcdTczQUZcdTU4ODNcdTUzQkJcdTk2NjQgZGVidWdnZXJcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGNodW5rU2l6ZVdhcm5pbmdMaW1pdDogMjAwMCwgLy8gY2h1bmsgXHU1OTI3XHU1QzBGXHU4QjY2XHU1NDRBXHU3Njg0XHU5NjUwXHU1MjM2XHVGRjA4XHU0RUU1IGticyBcdTRFM0FcdTUzNTVcdTRGNERcdUZGMDlcclxuICAgICAgcm9sbHVwT3B0aW9uczoge1xyXG4gICAgICAgIGV4dGVybmFsOiBPYmplY3Qua2V5cyhleHRlcm5hbEdsb2JhbHNPYmopLFxyXG4gICAgICAgIG91dHB1dDoge1xyXG4gICAgICAgICAgY2h1bmtGaWxlTmFtZXM6IGAke2Rpcn0vanMvY2h1bmsvW25hbWVdLVtoYXNoXS5qc2AsXHJcbiAgICAgICAgICBlbnRyeUZpbGVOYW1lczogYCR7ZGlyfS9qcy9lbnRyeS9bbmFtZV0tW2hhc2hdLmpzYCxcclxuICAgICAgICAgIGFzc2V0RmlsZU5hbWVzOiBgJHtkaXJ9L1tleHRdL1tuYW1lXS1baGFzaF0uW2V4dF1gXHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICAvKiBjc3M6IHtcclxuICAgIHByZXByb2Nlc3Nvck9wdGlvbnM6IHtcclxuICAgICAgc2Nzczoge1xyXG4gICAgICAgIC8vIEVsZW1lbnRQbHVzXHU4MUVBXHU1MkE4XHU1QkZDXHU1MTY1XHU1QjlBXHU1MjM2XHU1MzE2XHU2ODM3XHU1RjBGXHU2NTg3XHU0RUY2XHU4RkRCXHU4ODRDXHU2ODM3XHU1RjBGXHU4OTg2XHU3NkQ2ICBAdXNlIFwiQC9hc3NldHMvc3R5bGVzL2VsZW1lbnQuc2Nzc1wiIGFzICo7XHJcbiAgICAgICAgLy8gXHU1MTY4XHU1QzQwXHU2ODM3XHU1RjBGXHU1M0Q4XHU5MUNGICBAdXNlIFwiQC9hc3NldHMvc3R5bGVzL3Zhci5zY3NzXCIgYXMgKjs7XHJcbiAgICAgICAgYWRkaXRpb25hbERhdGE6IGBcclxuICAgICAgICAgIEB1c2UgXCJAL2Fzc2V0cy9zdHlsZXMvdmFyLnNjc3NcIiBhcyAqO1xyXG4gICAgICAgIGAsXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9ICovXHJcbiAgfVxyXG59KVxyXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQXVQLFNBQVMsZUFBZSxXQUFXO0FBQzFSLFNBQVMsb0JBQW9CO0FBQzdCLE9BQU8sU0FBUztBQUNoQixPQUFPLGdCQUFnQjtBQUN2QixPQUFPLGdCQUFnQjtBQUN2QixTQUFTLG9CQUFvQjtBQUM3QixPQUFPLHFCQUFxQjtBQUM1QixTQUFTLHdCQUF3QjtBQUVqQyxPQUFPLHFCQUFxQjtBQVQySCxJQUFNLDJDQUEyQztBQVV4TSxTQUFTLFNBQVM7QUFDaEIsUUFBTSxJQUFJLG9CQUFJLEtBQUs7QUFDbkIsUUFBTSxLQUFLLEVBQUUsWUFBWTtBQUN6QixRQUFNLEtBQUssRUFBRSxTQUFTLElBQUksS0FBSyxLQUFLLEVBQUUsU0FBUyxJQUFJLElBQUksS0FBSyxPQUFPLEVBQUUsU0FBUyxJQUFJO0FBQ2xGLFFBQU0sS0FBSyxFQUFFLFFBQVEsS0FBSyxLQUFLLEVBQUUsUUFBUSxJQUFJLEtBQUssTUFBTSxFQUFFLFFBQVE7QUFDbEUsUUFBTSxJQUFJLEVBQUUsU0FBUyxLQUFLLEtBQUssRUFBRSxTQUFTLElBQUksS0FBSyxNQUFNLEVBQUUsU0FBUztBQUNwRSxRQUFNLEtBQUssRUFBRSxXQUFXLEtBQUssS0FBSyxFQUFFLFdBQVcsSUFBSSxLQUFLLE1BQU0sRUFBRSxXQUFXO0FBQzNFLFFBQU0sVUFBVSxLQUFLLEtBQUssS0FBSyxJQUFJO0FBQ25DLFNBQU8sVUFBVTtBQUNuQjtBQUNBLElBQU0sTUFBTSxPQUFPO0FBRW5CLElBQU0sTUFBTTtBQUFBLEVBQ1YsS0FBSyxDQUFDLDBEQUEwRDtBQUFBLEVBQ2hFLElBQUk7QUFBQSxJQUNGO0FBQUEsSUFDQTtBQUFBLElBQ0E7QUFBQSxJQUNBO0FBQUEsSUFDQTtBQUFBLElBQ0E7QUFBQSxJQUNBO0FBQUEsSUFDQTtBQUFBLElBQ0E7QUFBQSxJQUNBO0FBQUEsSUFDQTtBQUFBLEVBQ0Y7QUFDRjtBQUVBLElBQU0scUJBQXFCO0FBQUEsRUFDekIsS0FBSztBQUFBLEVBQ0wsT0FBTztBQUFBLEVBQ1AsK0JBQStCO0FBQUEsRUFDL0IsY0FBYztBQUFBLEVBQ2QsT0FBTztBQUFBLEVBQ1AsTUFBTTtBQUFBLEVBQ04sTUFBTTtBQUFBLEVBQ04sUUFBUTtBQUFBLEVBQ1IsYUFBYTtBQUFBLEVBQ2IsTUFBTTtBQUFBLEVBQ04sZ0JBQWdCO0FBQ2xCO0FBRUEsSUFBTyxzQkFBUSxhQUFhLENBQUMsRUFBRSxLQUFLLE1BQU07QUFDeEMsVUFBUSxJQUFJLElBQUk7QUFDaEIsUUFBTSxlQUFlLFNBQVM7QUFDOUIsU0FBTztBQUFBLElBQ0wsU0FBUztBQUFBLE1BQ1AsSUFBSTtBQUFBLE1BQ0osV0FBVztBQUFBLFFBQ1QsV0FBVyxDQUFDLGFBQWEsQ0FBQztBQUFBLE1BQzVCLENBQUM7QUFBQSxNQUNELFdBQVc7QUFBQSxRQUNULFdBQVcsQ0FBQyxhQUFhLENBQUM7QUFBQSxNQUM1QixDQUFDO0FBQUEsTUFDRCxpQkFBaUI7QUFBQSxRQUNmLFFBQVE7QUFBQSxVQUNOLE1BQU07QUFBQSxZQUNKLFFBQVEsZUFBZSxJQUFJLE1BQU0sQ0FBQztBQUFBLFlBQ2xDLE9BQU8sZUFBZSxJQUFJLEtBQUssQ0FBQztBQUFBLFVBQ2xDO0FBQUEsUUFDRjtBQUFBLE1BQ0YsQ0FBQztBQUFBO0FBQUEsTUFFRCxnQkFBZ0I7QUFBQSxRQUNkLFNBQVM7QUFBQSxRQUNULFNBQVM7QUFBQSxRQUNULFdBQVc7QUFBQSxRQUNYLFdBQVc7QUFBQSxRQUNYLEtBQUs7QUFBQSxNQUNQLENBQUM7QUFBQSxNQUNEO0FBQUEsUUFDRSxHQUFHLGdCQUFnQixrQkFBa0I7QUFBQSxRQUNyQyxTQUFTO0FBQUEsUUFDVCxPQUFPO0FBQUEsTUFDVDtBQUFBLElBQ0Y7QUFBQSxJQUNBLFNBQVM7QUFBQSxNQUNQLE9BQU87QUFBQSxRQUNMLEtBQUssY0FBYyxJQUFJLElBQUksU0FBUyx3Q0FBZSxDQUFDO0FBQUEsUUFDcEQsUUFBUSxjQUFjLElBQUksSUFBSSxvQkFBb0Isd0NBQWUsQ0FBQztBQUFBLE1BQ3BFO0FBQUEsTUFDQSxZQUFZLENBQUMsUUFBUSxPQUFPLE9BQU8sUUFBUSxRQUFRLFNBQVMsTUFBTTtBQUFBLElBQ3BFO0FBQUEsSUFDQSxPQUFPO0FBQUEsTUFDTCxRQUFRO0FBQUEsTUFDUixXQUFXO0FBQUEsTUFDWCxRQUFRO0FBQUE7QUFBQSxNQUNSLGVBQWU7QUFBQSxRQUNiLFVBQVU7QUFBQSxVQUNSLGVBQWU7QUFBQTtBQUFBLFVBQ2YsY0FBYztBQUFBO0FBQUEsVUFDZCxlQUFlO0FBQUE7QUFBQSxRQUNqQjtBQUFBLE1BQ0Y7QUFBQSxNQUNBLHVCQUF1QjtBQUFBO0FBQUEsTUFDdkIsZUFBZTtBQUFBLFFBQ2IsVUFBVSxPQUFPLEtBQUssa0JBQWtCO0FBQUEsUUFDeEMsUUFBUTtBQUFBLFVBQ04sZ0JBQWdCLEdBQUcsR0FBRztBQUFBLFVBQ3RCLGdCQUFnQixHQUFHLEdBQUc7QUFBQSxVQUN0QixnQkFBZ0IsR0FBRyxHQUFHO0FBQUEsUUFDeEI7QUFBQSxNQUNGO0FBQUEsSUFDRjtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQVlGO0FBQ0YsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..4b1c897 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,14 @@ +import { fileURLToPath } from 'node:url' +import { mergeConfig, defineConfig, configDefaults } from 'vitest/config' +import viteConfig from './vite.config' + +export default mergeConfig( + viteConfig, + defineConfig({ + test: { + environment: 'jsdom', + exclude: [...configDefaults.exclude, 'e2e/**'], + root: fileURLToPath(new URL('./', import.meta.url)) + } + }) +)