diff --git a/.docker-compose/nginx/conf.d/my.conf b/.docker-compose/nginx/conf.d/my.conf
new file mode 100644
index 0000000..28e40c1
--- /dev/null
+++ b/.docker-compose/nginx/conf.d/my.conf
@@ -0,0 +1,28 @@
+client_max_body_size 100m;
+server {
+ listen 8080;
+ server_name localhost;
+ proxy_read_timeout 3600;
+ proxy_send_timeout 3600;
+ #charset koi8-r;
+ #access_log logs/host.access.log main;
+
+ location / {
+ root /usr/share/nginx/html;
+ add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
+ try_files $uri $uri/ /index.html;
+ }
+
+ location /api {
+ proxy_set_header Host $http_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ rewrite ^/api/(.*)$ /$1 break; #重写
+ proxy_pass http://129.204.101.51:9999; # 设置代理服务器的协议和地址
+ }
+
+ location /api/swagger/index.html {
+ proxy_pass http://129.204.101.51:9999/swagger/index.html;
+ }
+ }
\ No newline at end of file
diff --git a/.env.development b/.env.development
new file mode 100644
index 0000000..c6c0864
--- /dev/null
+++ b/.env.development
@@ -0,0 +1,5 @@
+# 变量必须以 VITE_ 为前缀才能暴露给外部读取
+NODE_ENV = 'development'
+VITE_APP_BASE_API = 'https://tools.1024tool.vip/'
+VITE_SERVE = "https://tools.1024tool.vip/"
+# VITE_SERVE = "http://192.168.210.29:18100/"
diff --git a/.env.production b/.env.production
new file mode 100644
index 0000000..a092139
--- /dev/null
+++ b/.env.production
@@ -0,0 +1,5 @@
+# 变量必须以 VITE_ 为前缀才能暴露给外部读取
+NODE_ENV = 'production'
+VITE_APP_BASE_API = 'https://tools.1024tool.vip/'
+VITE_SERVE = "https://tools.1024tool.vip/"
+
diff --git a/.env.test b/.env.test
new file mode 100644
index 0000000..0f8c01f
--- /dev/null
+++ b/.env.test
@@ -0,0 +1,5 @@
+NODE_ENV = 'development'
+
+VITE_APP_BASE_API = 'http://129.204.101.51:9999/'
+VITE_SERVE = "http://129.204.101.51:9999/"
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..24f3c90
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,25 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+components.d.ts
+node_modules
+dist
+dist-ssr
+*.local
+.vite
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+*.zip
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..67b7d4d
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,24 @@
+FROM registry.cn-shanghai.aliyuncs.com/server1024/node:16.17.1 AS build-stage
+
+WORKDIR /mht/
+COPY . .
+
+# 设置国内源并调试网络问题
+RUN npm config set registry https://registry.npmmirror.com
+RUN npm config set network-timeout 600000
+
+RUN npm install --legacy-peer-deps
+# 确保 vite 已经全局安装
+RUN npm install -g vite
+
+# 检查 vite 是否安装成功
+RUN vite --version
+
+# 运行构建命令
+RUN npm run build:prod
+
+FROM registry.cn-shanghai.aliyuncs.com/server1024/nginx:base AS production-stage
+
+COPY .docker-compose/nginx/conf.d/my.conf /etc/nginx/conf.d/my.conf
+COPY --from=build-stage /mht/dist /usr/share/nginx/html
+RUN ls -al /usr/share/nginx/html
\ No newline at end of file
diff --git a/auto-imports.d.ts b/auto-imports.d.ts
new file mode 100644
index 0000000..f5dae85
--- /dev/null
+++ b/auto-imports.d.ts
@@ -0,0 +1,11 @@
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// noinspection JSUnusedGlobalSymbols
+// Generated by unplugin-auto-import
+export {}
+declare global {
+ const ElMessage: typeof import('element-plus/es')['ElMessage']
+ const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
+ const ElNotification: typeof import('element-plus/es')['ElNotification']
+}
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..8e49c8a
--- /dev/null
+++ b/index.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+ 儿科后台管理
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..56c81a7
--- /dev/null
+++ b/package.json
@@ -0,0 +1,69 @@
+{
+ "name": "mihoutao",
+ "private": true,
+ "version": "1.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite --open",
+ "build": "vite build",
+ "build:test": "vite build --mode test",
+ "build:prod": "vite build --mode production",
+ "build:no": "vite build",
+ "preview": "vite preview",
+ "lint": "eslint src",
+ "fix": "eslint src --fix",
+ "format": "prettier --write \"./**/*.{html,vue,ts,js,json,md}\"",
+ "lint:eslint": "eslint src/**/*.{ts,vue} --cache --fix",
+ "lint:style": "stylelint src/**/*.{css,scss,vue} --cache --fix"
+ },
+ "dependencies": {
+ "@element-plus/icons-vue": "2.1.0",
+ "@wangeditor/editor": "^5.1.23",
+ "@wangeditor/editor-for-vue": "next",
+ "axios": "^1.4.0",
+ "default-passive-events": "^2.0.0",
+ "element-plus": "^2.8.0",
+ "mitt": "^3.0.1",
+ "moment": "^2.29.4",
+ "nprogress": "^0.2.0",
+ "pinia": "^2.1.6",
+ "pinia-plugin-persistedstate": "^3.2.0",
+ "vue": "^3.3.4",
+ "vue-router": "^4.2.4"
+ },
+ "devDependencies": {
+ "@babel/eslint-parser": "^7.22.9",
+ "@iconify-json/ep": "^1.1.11",
+ "@typescript-eslint/eslint-plugin": "^6.2.0",
+ "@typescript-eslint/parser": "^6.2.0",
+ "@vitejs/plugin-vue": "^4.2.3",
+ "eslint": "^8.45.0",
+ "eslint-plugin-import": "^2.27.5",
+ "eslint-plugin-node": "^11.1.0",
+ "eslint-plugin-vue": "^9.15.1",
+ "mockjs": "^1.1.0",
+ "postcss": "^8.4.27",
+ "postcss-html": "^1.5.0",
+ "postcss-pxtorem": "^6.1.0",
+ "postcss-scss": "^4.0.6",
+ "sass": "^1.64.1",
+ "sass-loader": "^13.3.2",
+ "stylelint": "^15.10.2",
+ "stylelint-config-prettier": "^9.0.5",
+ "stylelint-config-recess-order": "^4.3.0",
+ "stylelint-config-recommended-scss": "^12.0.0",
+ "stylelint-config-standard": "^34.0.0",
+ "stylelint-config-standard-scss": "^10.0.0",
+ "stylelint-config-standard-vue": "^1.0.0",
+ "stylelint-order": "^6.0.3",
+ "stylelint-scss": "^5.0.1",
+ "typescript": "^5.0.2",
+ "unplugin-auto-import": "^0.16.6",
+ "unplugin-icons": "^0.16.5",
+ "unplugin-vue-components": "^0.25.1",
+ "vite": "^4.4.5",
+ "vite-plugin-mock": "2.9.6",
+ "vite-plugin-svg-icons": "^2.0.1",
+ "vue-tsc": "^1.8.5"
+ }
+}
diff --git a/postcss.config.cjs b/postcss.config.cjs
new file mode 100644
index 0000000..6944eee
--- /dev/null
+++ b/postcss.config.cjs
@@ -0,0 +1,8 @@
+module.exports = {
+ // plugins: {
+ // 'postcss-pxtorem': {
+ // rootValue: 100, // 设置根元素字体大小,1rem = 16px
+ // propList: ['*'], // 将所有属性都转换成rem
+ // },
+ // },
+ };
\ No newline at end of file
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..9e29126
Binary files /dev/null and b/public/favicon.ico differ
diff --git a/src/App.vue b/src/App.vue
new file mode 100644
index 0000000..36bb8c0
--- /dev/null
+++ b/src/App.vue
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/api/login.ts b/src/api/login.ts
new file mode 100644
index 0000000..3b05b41
--- /dev/null
+++ b/src/api/login.ts
@@ -0,0 +1,91 @@
+import request from '@/utils/request'
+
+
+// 登录
+export const userLogin = (data: any) => {
+ return request({
+ url: `api/customer/login`,
+ method: 'post',
+ data
+ })
+}
+// 注册
+export const userRegister = (data: any) => {
+ return request({
+ url: `api/customer/register`,
+ method: 'post',
+ data
+ })
+}
+
+// 发送邮箱验证码
+export const sendEmail = (data: any) => {
+ return request({
+ url: `api/customer/send_activation_email`,
+ method: 'post',
+ data
+ })
+}
+
+// 获取用户信息
+export const getUserInfo = () => {
+ return request({
+ url: `api/customer/info`,
+ method: 'get'
+ })
+}
+
+// 激活账号
+
+export const verifyCode = (code: any) => {
+ return request({
+ url: `api/customer/verify_activation_email/${code}`,
+ method: 'get'
+ })
+}
+
+// 扫码登录
+
+export const getRr = (data) => {
+ return request({
+ url: `api/customer/qr`,
+ method: 'post',
+ data
+ })
+}
+
+// 轮询登录结果
+
+export const getRrLogin = (data) => {
+ return request({
+ url: `api/customer/qr_login`,
+ method: 'post',
+ data
+ })
+}
+
+
+export const getUserSearch = (data) => {
+ return request({
+ url: `api/customer/search`,
+ method: 'post',
+ data
+ })
+}
+
+export const setUserRecharge = (data) => {
+ return request({
+ url: `api/customer/recharge`,
+ method: 'post',
+ data
+ })
+}
+
+// 充值记录
+export const rechargeList = (data) => {
+ return request({
+ url: `api/customer/recharge/list`,
+ method: 'get',
+ params: data
+ })
+}
\ No newline at end of file
diff --git a/src/api/news.ts b/src/api/news.ts
new file mode 100644
index 0000000..3b05b41
--- /dev/null
+++ b/src/api/news.ts
@@ -0,0 +1,91 @@
+import request from '@/utils/request'
+
+
+// 登录
+export const userLogin = (data: any) => {
+ return request({
+ url: `api/customer/login`,
+ method: 'post',
+ data
+ })
+}
+// 注册
+export const userRegister = (data: any) => {
+ return request({
+ url: `api/customer/register`,
+ method: 'post',
+ data
+ })
+}
+
+// 发送邮箱验证码
+export const sendEmail = (data: any) => {
+ return request({
+ url: `api/customer/send_activation_email`,
+ method: 'post',
+ data
+ })
+}
+
+// 获取用户信息
+export const getUserInfo = () => {
+ return request({
+ url: `api/customer/info`,
+ method: 'get'
+ })
+}
+
+// 激活账号
+
+export const verifyCode = (code: any) => {
+ return request({
+ url: `api/customer/verify_activation_email/${code}`,
+ method: 'get'
+ })
+}
+
+// 扫码登录
+
+export const getRr = (data) => {
+ return request({
+ url: `api/customer/qr`,
+ method: 'post',
+ data
+ })
+}
+
+// 轮询登录结果
+
+export const getRrLogin = (data) => {
+ return request({
+ url: `api/customer/qr_login`,
+ method: 'post',
+ data
+ })
+}
+
+
+export const getUserSearch = (data) => {
+ return request({
+ url: `api/customer/search`,
+ method: 'post',
+ data
+ })
+}
+
+export const setUserRecharge = (data) => {
+ return request({
+ url: `api/customer/recharge`,
+ method: 'post',
+ data
+ })
+}
+
+// 充值记录
+export const rechargeList = (data) => {
+ return request({
+ url: `api/customer/recharge/list`,
+ method: 'get',
+ params: data
+ })
+}
\ No newline at end of file
diff --git a/src/assets/images/error_images/401.png b/src/assets/images/error_images/401.png
new file mode 100644
index 0000000..90bbf6e
Binary files /dev/null and b/src/assets/images/error_images/401.png differ
diff --git a/src/assets/images/error_images/404.png b/src/assets/images/error_images/404.png
new file mode 100644
index 0000000..14fa725
Binary files /dev/null and b/src/assets/images/error_images/404.png differ
diff --git a/src/assets/images/error_images/cloud.png b/src/assets/images/error_images/cloud.png
new file mode 100644
index 0000000..247c06b
Binary files /dev/null and b/src/assets/images/error_images/cloud.png differ
diff --git a/src/assets/images/logo.svg b/src/assets/images/logo.svg
new file mode 100644
index 0000000..35201ee
--- /dev/null
+++ b/src/assets/images/logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/images/welcome.png b/src/assets/images/welcome.png
new file mode 100644
index 0000000..df138ab
Binary files /dev/null and b/src/assets/images/welcome.png differ
diff --git a/src/components/SvgIcon/index.vue b/src/components/SvgIcon/index.vue
new file mode 100644
index 0000000..bbc6500
--- /dev/null
+++ b/src/components/SvgIcon/index.vue
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/index.ts b/src/components/index.ts
new file mode 100644
index 0000000..61704b3
--- /dev/null
+++ b/src/components/index.ts
@@ -0,0 +1,20 @@
+import SvgIcon from './SvgIcon/index.vue'
+
+import type { App, Component } from 'vue'
+
+import * as ElementPlusIconsVue from '@element-plus/icons-vue'
+
+const allGlobalComponent: Component = { SvgIcon }
+
+export default {
+ install(app: App) {
+ Object.keys(allGlobalComponent).forEach((key: string) => {
+ // 注册为全局组件
+ app.component(key, allGlobalComponent[key])
+ })
+ // 将 element-plus 的图标注册为全局组件
+ for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+ app.component(key, component)
+ }
+ },
+}
diff --git a/src/directive/has.ts b/src/directive/has.ts
new file mode 100644
index 0000000..9446eee
--- /dev/null
+++ b/src/directive/has.ts
@@ -0,0 +1,15 @@
+// 按钮权限的实现
+import pinia from '@/store'
+import useUserStore from '@/store/modules/user'
+
+export const isHasButton = (app: any) => {
+ // 自定义指令
+ app.directive('has', {
+ mounted(el: any, options: any) {
+ const userStore = useUserStore(pinia)
+ if (!userStore.buttons.includes(options.value)) {
+ el.parentNode.removeChild(el)
+ }
+ }
+ })
+}
\ No newline at end of file
diff --git a/src/eventBus/index.ts b/src/eventBus/index.ts
new file mode 100644
index 0000000..1a13398
--- /dev/null
+++ b/src/eventBus/index.ts
@@ -0,0 +1,2 @@
+import mitt from 'mitt'
+export default mitt()
\ No newline at end of file
diff --git a/src/layout/header/index.vue b/src/layout/header/index.vue
new file mode 100644
index 0000000..f90e254
--- /dev/null
+++ b/src/layout/header/index.vue
@@ -0,0 +1,129 @@
+
+
+
+
+ 猕猴桃
+
+
+
+
+
+
+
+
diff --git a/src/layout/home/header.vue b/src/layout/home/header.vue
new file mode 100644
index 0000000..9d8c566
--- /dev/null
+++ b/src/layout/home/header.vue
@@ -0,0 +1,120 @@
+
+
+
+
+
+
diff --git a/src/layout/home/index.vue b/src/layout/home/index.vue
new file mode 100644
index 0000000..682c059
--- /dev/null
+++ b/src/layout/home/index.vue
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/layout/home/menu.vue b/src/layout/home/menu.vue
new file mode 100644
index 0000000..a43b65c
--- /dev/null
+++ b/src/layout/home/menu.vue
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+ 患者管理
+
+
+
+ 健康文章
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/layout/setting/index.vue b/src/layout/setting/index.vue
new file mode 100644
index 0000000..562bb4f
--- /dev/null
+++ b/src/layout/setting/index.vue
@@ -0,0 +1,133 @@
+
+
+ 退出
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main.ts b/src/main.ts
new file mode 100644
index 0000000..7e67e15
--- /dev/null
+++ b/src/main.ts
@@ -0,0 +1,40 @@
+import { createApp } from 'vue'
+import App from './App.vue'
+// import 'virtual:svg-icons-register'
+import globalComponent from './components/index'
+import { router } from './router'
+import pinia from './store'
+import ElementPlus from 'element-plus'
+import zhCn from 'element-plus/es/locale/lang/zh-cn'
+// default-passive-events 是一个轻量级的 JavaScript 库,旨在通过自动为支持 EventListenerOptions 的浏览器设置事件监听器的 { passive: true } 选项来优化滚动性能。这有助于减少触摸和滚轮事件处理时的延迟,从而提升用户体验。
+import 'default-passive-events'
+
+// 修改主题必须文件
+import 'element-plus/theme-chalk/src/index.scss'
+
+// 重置样式
+import '@/styles/index.scss'
+
+// 暗黑模式
+import 'element-plus/theme-chalk/dark/css-vars.css'
+
+// 自定义指令
+// import { isHasButton } from '@/directive/has.ts'
+
+// 屏幕自适应
+// import './utils/fitter.js'
+
+
+
+const app = createApp(App)
+app.use(ElementPlus, {
+ locale: zhCn,
+ })
+app.use(globalComponent)
+app.use(pinia)
+app.use(router)
+app.mount('#app')
+
+
+
+
\ No newline at end of file
diff --git a/src/router/index.ts b/src/router/index.ts
new file mode 100644
index 0000000..b179c02
--- /dev/null
+++ b/src/router/index.ts
@@ -0,0 +1,106 @@
+import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
+// import Nprogress from 'nprogress'
+// import 'nprogress/nprogress.css'
+import { GET_TOKEN } from '@/utils/token'
+
+
+const constantRoutes = [
+
+ {
+ path: '/',
+ name: 'Layout',
+ component: () => import('@/layout/home/index.vue'),
+ redirect: '/user',
+ meta: {
+ title: '首页'
+ },
+ children: [
+ {
+ path: '/user',
+ name: 'user',
+ component: () => import('@/views/user/index.vue'),
+ meta: {
+ title: '患者管理'
+ },
+ },
+ {
+ path: '/news',
+ name: 'news',
+ component: () => import('@/views/news/index.vue'),
+ meta: {
+ title: '文章管理'
+ },
+ },
+ {
+ path: '/addNew',
+ name: 'addNew',
+ component: () => import('@/views/news/addNew.vue'),
+ meta: {
+ title: '添加文章'
+ },
+ },
+ ],
+ },
+
+ {
+ path: '/login',
+ name: 'login',
+ component: () => import('@/views/login/index.vue'),
+ meta: {
+ title: '登录',
+ },
+ },
+
+ {
+ path: '/:pathMatch(.*)',
+ name: 'Any',
+ component: () => import('@/views/404/index.vue'),
+ meta: {
+ title: '404',
+ hidden: true,
+ icon: '',
+ },
+ }
+]
+
+
+
+export const router = createRouter({
+ history: createWebHistory(),
+ routes: constantRoutes,
+ // 切换路由跳转到顶部
+ scrollBehavior() {
+ return {
+ left: 0,
+ top: 0
+ }
+ }
+})
+
+const whiteList = ['/login']
+
+// 全局前置守卫
+router.beforeEach(async (to, from, next) => {
+ // Nprogress.start()
+ const token = GET_TOKEN()
+ console.log(token)
+ if (token) {
+ if (to.path === '/login') {
+ next('/index')
+ } else {
+ next()
+ }
+ } else {
+ if (whiteList.indexOf(to.path) > -1) {
+ next()
+ } else {
+ next('/login')
+ }
+ }
+})
+
+// 全局后置守卫
+router.afterEach(() => {
+ // Nprogress.done()
+})
+
diff --git a/src/store/index.ts b/src/store/index.ts
new file mode 100644
index 0000000..4b22a06
--- /dev/null
+++ b/src/store/index.ts
@@ -0,0 +1,8 @@
+// 大仓库
+import { createPinia } from 'pinia'
+import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
+
+const pinia = createPinia()
+pinia.use(piniaPluginPersistedstate)
+
+export default pinia
diff --git a/src/store/modules/store.ts b/src/store/modules/store.ts
new file mode 100644
index 0000000..71db30b
--- /dev/null
+++ b/src/store/modules/store.ts
@@ -0,0 +1,13 @@
+import { defineStore } from "pinia"
+
+const pinia = defineStore('main', {
+ state: () => ({
+
+ }),
+
+ actions: {
+
+ }
+})
+
+export default pinia
\ No newline at end of file
diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts
new file mode 100644
index 0000000..b95656b
--- /dev/null
+++ b/src/store/modules/user.ts
@@ -0,0 +1,33 @@
+// 用户相关仓库
+import { defineStore } from 'pinia'
+import { ref } from 'vue'
+import { REMOVE_TOKEN } from '@/utils/token'
+
+const useUserStore = defineStore(
+ 'mihoutaoUser',
+ () => {
+ const userInfo = ref({})
+ const token = ref('')
+ const getUserInfo = async (data: any) => {
+ userInfo.value = data
+ }
+
+ const userLogout = async () => {
+ //退出登录请求
+ REMOVE_TOKEN()
+ userInfo.value = {}
+ }
+
+ return {
+ userInfo,
+ token,
+ getUserInfo,
+ userLogout,
+ }
+ },
+ {
+ persist: true
+ }
+)
+
+export default useUserStore
diff --git a/src/styles/element/index.scss b/src/styles/element/index.scss
new file mode 100644
index 0000000..153dbed
--- /dev/null
+++ b/src/styles/element/index.scss
@@ -0,0 +1,11 @@
+// styles/element/index.scss
+/* 只需要重写你需要的即可 */
+@forward 'element-plus/theme-chalk/src/common/var.scss' with (
+ $colors: (
+ 'primary': (
+ 'base': #5CC4A7,
+ ),
+ ),
+
+);
+
diff --git a/src/styles/index.scss b/src/styles/index.scss
new file mode 100644
index 0000000..c4428f2
--- /dev/null
+++ b/src/styles/index.scss
@@ -0,0 +1,111 @@
+//引入清除默认样式
+@import './reset.scss';
+
+:root {
+ // --my-shadow: 0 1px 3px 0 rgba(0, 0, 0, .1);
+ // --my-shadow-large: 0px 0px 3px 5px var(--el-border-color);
+ // --my-shadow-small: 0px 0px 0px 1px var(--el-border-color);
+ --my-border-radius: 8px;
+ --my-border-radius-large: 16px;
+ --my-border-radius-small: 4px;
+ --el-fill-color-blank: rgba(240, 245, 245, 1) !important;
+}
+
+//滚动条外观设置
+::-webkit-scrollbar {
+ width: 5px;
+ height: 5px;
+}
+
+::-webkit-scrollbar-thumb {
+ width: 10px;
+ background-color: #c4cbd7;
+ border-radius: 10px;
+}
+
+.el-popover.el-popper {
+ border-radius: var(--my-border-radius) !important;
+}
+
+.el-popper__arrow {
+ display: none;
+}
+
+.el-popper.is-light {
+ border: 0;
+}
+
+.pagination-box {
+ // overflow: hidden;
+ margin-top: 25px;
+ text-align: right;
+ height: 30px;
+
+ .el-pagination {
+ float: right;
+ }
+}
+.el-table {
+ --el-table-header-text-color: var(--el-color-primary) !important;
+ --el-table-bg-color: var(--el-bg-color) !important;
+}
+
+
+.el-input {
+ --el-select-input-focus-border-color: none !important;
+ --el-input-bg-color: rgba(240, 245, 245, 1) !important;
+ --el-input-focus-border-color: none;
+ --el-input-border-color: none !important;
+}
+
+.el-input__wrapper.is-focus,
+.el-select__wrapper.is-focused,
+.el-select__wrapper {
+ box-shadow: none !important;
+}
+
+.el-textarea,
+.el-date-editor,
+.el-cascader {
+ --el-input-focus-border-color: none !important;
+ --el-input-bg-color: rgba(240, 245, 245, 1) !important;
+ --el-input-border-color: none !important;
+}
+
+.el-input__wrapper:hover,
+.el-textarea__inner:hover,
+.el-input-group__append:hover,
+.el-input-group__prepend:hover {
+ box-shadow: none !important;
+
+}
+
+.el-input__wrapper.is-focus,
+.el-textarea__inner:focus,
+.el-date-editor.is-active {
+ box-shadow: none !important;
+
+}
+
+.el-input-group__append,
+.el-input-group__prepend {
+ background: var(--el-input-bg-color) !important;
+ box-shadow: none !important;
+ padding: 0 10px !important;
+}
+
+
+.el-textarea,
+.el-date-editor,
+.el-cascader {
+ --el-input-focus-border-color: none !important;
+ --el-input-border-color: none !important;
+}
+
+.el-radio{
+ --el-radio-input-bg-color: #fff!important;
+}
+
+.el-descriptions__body {
+ background-color: #fff!important;
+}
\ No newline at end of file
diff --git a/src/styles/reset.scss b/src/styles/reset.scss
new file mode 100644
index 0000000..d80fa75
--- /dev/null
+++ b/src/styles/reset.scss
@@ -0,0 +1,202 @@
+*,
+*:after,
+*:before {
+ box-sizing: border-box;
+ outline: none;
+ font-family: Avenir, Helvetica, Arial, sans-serif;
+}
+
+html {
+ height: 100%;
+ font-size: 16px;
+ // min-width: 1300px;
+}
+
+html,
+body,
+div,
+span,
+applet,
+object,
+iframe,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+p,
+blockquote,
+pre,
+a,
+abbr,
+acronym,
+address,
+big,
+cite,
+code,
+del,
+dfn,
+em,
+img,
+ins,
+kbd,
+q,
+s,
+samp,
+small,
+strike,
+strong,
+sub,
+sup,
+tt,
+var,
+b,
+u,
+i,
+center,
+dl,
+dt,
+dd,
+ol,
+ul,
+li,
+fieldset,
+form,
+label,
+legend,
+table,
+caption,
+tbody,
+tfoot,
+thead,
+tr,
+th,
+td,
+article,
+aside,
+canvas,
+details,
+embed,
+figure,
+figcaption,
+footer,
+header,
+hgroup,
+menu,
+nav,
+output,
+ruby,
+section,
+summary,
+time,
+mark,
+audio,
+video {
+ // font: inherit;
+
+ font-size: 100%;
+
+ margin: 0;
+ padding: 0;
+
+ vertical-align: baseline;
+
+ border: 0;
+}
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section {
+ display: block;
+}
+
+body {
+ height: 100%;
+ line-height: 1;
+ font-size: 16px;
+ color: var(--el-text-color-regular);
+}
+
+ol,
+ul {
+ list-style: none;
+}
+
+blockquote,
+q {
+ quotes: none;
+
+ &:before,
+ &:after {
+ content: '';
+ content: none;
+ }
+}
+
+sub,
+sup {
+ font-size: 75%;
+ line-height: 0;
+
+ position: relative;
+
+ vertical-align: baseline;
+}
+
+sup {
+ top: -.5em;
+}
+
+sub {
+ bottom: -.25em;
+}
+
+table {
+ border-spacing: 0;
+ border-collapse: collapse;
+}
+
+input,
+textarea,
+button {
+ // font-family: inhert;
+ font-family: Avenir, Helvetica, Arial, sans-serif;
+ font-size: inherit;
+
+ color: inherit;
+}
+
+select {
+ text-indent: .01px;
+ text-overflow: '';
+
+ border: 0;
+ border-radius: 0;
+
+ -webkit-appearance: none;
+ -moz-appearance: none;
+}
+
+select::-ms-expand {
+ display: none;
+}
+
+code,
+pre {
+ font-family: monospace, monospace;
+ font-size: 1em;
+}
+
+
+html.dark {
+ --el-text-color-regular: #606266;
+}
\ No newline at end of file
diff --git a/src/utils/fitter.js b/src/utils/fitter.js
new file mode 100644
index 0000000..d6c93da
--- /dev/null
+++ b/src/utils/fitter.js
@@ -0,0 +1,25 @@
+// 计算公式当前屏幕宽度winWidth除以设计稿宽度1920得到屏幕与设计稿的比例,根据比例设置根元素的fontSize,设计稿的100px等于1rem
+
+// 获取屏幕宽度
+var winWidth = document.documentElement.offsetWidth || document.body.offsetWidth
+// 获取html跟元素
+var oHtml = document.getElementsByTagName('html')[0]
+// oHtml.style.fontSize = 100 * winWidth / 1680 + 'px'
+if(winWidth > 1680){
+ oHtml.style.fontSize = 100 * 1680 / 1680 + 'px'
+} else {
+ oHtml.style.fontSize = 100 * winWidth / 1680 + 'px'
+}
+
+// 页面大小发生变化事重新设置
+window.addEventListener('resize', function() {
+ var winWidth = document.documentElement.offsetWidth || document.body.offsetWidth
+ var oHtml = document.getElementsByTagName('html')[0]
+
+ if(winWidth > 1680){
+ oHtml.style.fontSize = 100 * 1680 / 1680 + 'px'
+ } else {
+ oHtml.style.fontSize = 100 * winWidth / 1680 + 'px'
+ }
+
+})
\ No newline at end of file
diff --git a/src/utils/index.ts b/src/utils/index.ts
new file mode 100644
index 0000000..d0efc5b
--- /dev/null
+++ b/src/utils/index.ts
@@ -0,0 +1,55 @@
+/**
+ * @description 生成随机数
+ * @param {Number} min 最小值
+ * @param {Number} max 最大值
+ * @returns {Number}
+ */
+export function randomNum(min: number, max: number): number {
+ const num = Math.floor(Math.random() * (min - max) + max)
+ return num
+}
+
+/**
+ * rgba转#
+ * @param color 颜色
+ */
+export function getHexColor(color: string) {
+ const values = color
+ .replace(/rgba?\(/, '')
+ .replace(/\)/, '')
+ .replace(/[\s+]/g, '')
+ .split(',')
+ const a = parseFloat(values[3]),
+ r = Math.floor(a * parseInt(values[0]) + (1 - a) * 255),
+ g = Math.floor(a * parseInt(values[1]) + (1 - a) * 255),
+ b = Math.floor(a * parseInt(values[2]) + (1 - a) * 255)
+ return '#' +
+ ('0' + r.toString(16)).slice(-2) +
+ ('0' + g.toString(16)).slice(-2) +
+ ('0' + b.toString(16)).slice(-2)
+}
+
+/**
+ * 获取Component的方法
+ * @param view 路径
+ * @param type 路由层数 目前只兼容二级路由
+ */
+export function _getViews(view: any, type: any) {
+ let res;
+ let modules: any;
+ if (type == "one") {
+ modules = import.meta.glob("/src/view/*.vue");
+ } else {
+ modules = import.meta.glob("/src/view/**/*.vue");
+ }
+ for (const path in modules) {
+ const dir =
+ type == "one"
+ ? path.split("view/")[1].split(".vue")[0]
+ : path.split("view/")[1].split(".vue")[0];
+ if (dir === view) {
+ res = () => modules[path]();
+ }
+ }
+ return res;
+};
\ No newline at end of file
diff --git a/src/utils/request.ts b/src/utils/request.ts
new file mode 100644
index 0000000..b893b26
--- /dev/null
+++ b/src/utils/request.ts
@@ -0,0 +1,68 @@
+import axios from 'axios'
+import { ElNotification } from 'element-plus'
+import { GET_TOKEN, REMOVE_TOKEN } from '@/utils/token'
+import useUserStore from '@/store/modules/user'
+import pinia from '@/store'
+//创建axios实例
+const request = axios.create({
+ baseURL: import.meta.env.VITE_APP_BASE_API,
+ timeout: 120000,
+})
+// 存储需要取消的请求
+const requestsToCancel = {};
+
+//请求拦截器
+request.interceptors.request.use((config: any) => {
+ if (GET_TOKEN()) {
+ config.headers.Authorization = GET_TOKEN()
+ }
+ // 为每个请求生成一个唯一的cancel token
+ config.cancelToken = new axios.CancelToken(cancel => {
+ requestsToCancel[config.url] = cancel;
+ });
+ return config
+})
+//响应拦截器
+request.interceptors.response.use(
+ (response: any) => {
+ return response.data
+
+ },
+ (error: any) => {
+ //处理网络错误
+ if (error.response?.data.code == 20107) {
+ return
+ }
+ // if (error.response?.data.code == 10101) {
+ // Object.values(requestsToCancel).forEach(cancel => cancel());
+ // }
+ if (error.response?.data.code == 10103) {
+ Object.values(requestsToCancel).forEach(cancel => cancel());
+ const useUser = useUserStore(pinia)
+ useUser.userInfo = {}
+ useUser.token = ''
+ REMOVE_TOKEN()
+ ElNotification({
+ type: 'error',
+ message: error.response.data.message,
+ duration: 2000,
+ onClose: () => {
+ if(location.pathname != '/index'){
+ location.href = '/index'
+ }
+ }
+ })
+ return
+ }
+
+ ElNotification({
+ type: 'error',
+ message: error.response.data.message,
+ duration: 2000,
+ })
+
+ return Promise.reject(error)
+ },
+)
+
+export default request
diff --git a/src/utils/time.ts b/src/utils/time.ts
new file mode 100644
index 0000000..2458a2d
--- /dev/null
+++ b/src/utils/time.ts
@@ -0,0 +1,12 @@
+export const getTime = () => {
+ const hours = new Date().getHours()
+ if (hours <= 9) {
+ return '早上好 🌅'
+ } else if (hours <= 12) {
+ return '上午好 🌞'
+ } else if (hours <= 18) {
+ return '下午好 ☕️'
+ } else {
+ return '晚上好 🌛'
+ }
+}
diff --git a/src/utils/token.ts b/src/utils/token.ts
new file mode 100644
index 0000000..02fe731
--- /dev/null
+++ b/src/utils/token.ts
@@ -0,0 +1,18 @@
+export const SET_TOKEN = (token: string) => {
+ localStorage.setItem('TOKEN', token)
+}
+
+
+
+export const GET_TOKEN = () => {
+ return localStorage.getItem('TOKEN')
+}
+
+export const REMOVE_TOKEN = () => {
+ localStorage.removeItem('TOKEN')
+}
+export const SET_USER_TYPE = (type) => {
+ localStorage.removeItem('userType')
+}
+
+
diff --git a/src/utils/validate.ts b/src/utils/validate.ts
new file mode 100644
index 0000000..bdd4e18
--- /dev/null
+++ b/src/utils/validate.ts
@@ -0,0 +1,42 @@
+
+
+// 电话校验
+export const checkPhoneNumber = (rule: any, value: any, callback: any) => {
+ const reg = /^[1][3456789][0-9]{9}$/
+ if ((reg.test(value))) {
+ callback()
+ } else {
+ callback(new Error('validate.please_enter_the_correct_phone_number'))
+ }
+}
+// 验证邮箱的规则
+export const checkEmail = (rule: any, value: any, callback: any) => {
+ // 验证邮箱的正则表达式
+ const reg = /^[a-zA-Z0-9]+([\.-_][a-zA-Z0-9]+)*@[a-zA-Z0-9]+([\.-][a-zA-Z0-9]+)*\.[a-zA-Z]{2,}$/
+ if (reg.test(value)) {
+ callback()
+ }
+ callback(new Error('请输入正确的的邮箱'))
+}
+// 密码规则
+export const checkPassword= (rule: any, value: any, callback: any) => {
+
+ const reg = /^[a-zA-Z0-9\W]{6,10}$/
+ if (reg.test(value)) {
+ callback()
+ }
+ callback(new Error('你输入6到10位密码, 不能包含特殊符号'))
+}
+
+// 数字验证
+export const checkNumber = (rule: any, value: any, callback: any) => {
+ if(isNaN(value)){
+ callback(new Error('validate.please_enter_a_number_greater_than_0'))
+ } else {
+ if (value > 0) {
+ callback()
+ } else {
+ callback(new Error('validate.please_enter_a_number_greater_than_0'))
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/views/404/index.vue b/src/views/404/index.vue
new file mode 100644
index 0000000..332f814
--- /dev/null
+++ b/src/views/404/index.vue
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
您访问的页面不存在
+
请检查您的地址是否正确,或者点击下面按钮回到首页
+
+ 返回首页
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/adminPages/AICreation/index.vue b/src/views/adminPages/AICreation/index.vue
new file mode 100644
index 0000000..22e9f02
--- /dev/null
+++ b/src/views/adminPages/AICreation/index.vue
@@ -0,0 +1,769 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 字数: {{ characterCount }}
+
+
保存
+
存草稿
+
预览
+
+
+
+
+
+
+
+
+
+
{{ newTitle }}
+
+
+
+
+
+
+ 关闭
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/adminPages/hot/index.vue b/src/views/adminPages/hot/index.vue
new file mode 100644
index 0000000..f8c6324
--- /dev/null
+++ b/src/views/adminPages/hot/index.vue
@@ -0,0 +1,128 @@
+
+
+
+
+ AI
+ 热榜
+ 低粉高爆
+ 创作榜单
+
+
+ 关键词
+
+ 阅读量
+
+
+
+
+
+ 发布时间
+
+ 搜索
+
+
+
+
+
+
+
+
+
+
+
+
+ AI 改写
+ 查看原文
+ 创作灵感
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/adminPages/works/index.vue b/src/views/adminPages/works/index.vue
new file mode 100644
index 0000000..533dfdb
--- /dev/null
+++ b/src/views/adminPages/works/index.vue
@@ -0,0 +1,8 @@
+
+
+
热榜
+
+
+
diff --git a/src/views/home/index.vue b/src/views/home/index.vue
new file mode 100644
index 0000000..9cf39c8
--- /dev/null
+++ b/src/views/home/index.vue
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
{{ $t('common.welcome_to_use') }} {{ $t('common.carbonease') }} CarbonEasy
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/index/index.vue b/src/views/index/index.vue
new file mode 100644
index 0000000..2b9bcba
--- /dev/null
+++ b/src/views/index/index.vue
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
一站式公众号订阅
+
+
+ 开始使用
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/login/index.vue b/src/views/login/index.vue
new file mode 100644
index 0000000..7475031
--- /dev/null
+++ b/src/views/login/index.vue
@@ -0,0 +1,112 @@
+
+
+
+
+ 系统登录
+
+
+
+
+
+
+
+
+
+
+ 登录
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/news/addNew.vue b/src/views/news/addNew.vue
new file mode 100644
index 0000000..decb156
--- /dev/null
+++ b/src/views/news/addNew.vue
@@ -0,0 +1,760 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 字数: {{ characterCount }}
+
+
保存
+
存草稿
+
预览
+
+
+
+
+
+
+
+
+
+
{{ newTitle }}
+
+
+
+
+
+
+ 关闭
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/news/index.vue b/src/views/news/index.vue
new file mode 100644
index 0000000..c6e47f4
--- /dev/null
+++ b/src/views/news/index.vue
@@ -0,0 +1,48 @@
+
+
+
+
+ 搜索
+ 添加文章
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/user/index.vue b/src/views/user/index.vue
new file mode 100644
index 0000000..0928210
--- /dev/null
+++ b/src/views/user/index.vue
@@ -0,0 +1,40 @@
+
+
+
+
+ 搜索
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..a1d5e9f
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,31 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "module": "ESNext",
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "node",
+ "allowImportingTsExtensions": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "preserve",
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+
+ "baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
+ "paths": {
+ //路径映射,相对于baseUrl
+ "@/*": ["src/*"]
+ },
+ },
+ "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
+ "references": [{ "path": "./tsconfig.node.json" }]
+}
diff --git a/tsconfig.node.json b/tsconfig.node.json
new file mode 100644
index 0000000..42872c5
--- /dev/null
+++ b/tsconfig.node.json
@@ -0,0 +1,10 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "skipLibCheck": true,
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "allowSyntheticDefaultImports": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..d9c07a4
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,90 @@
+import { defineConfig, loadEnv } from 'vite'
+import vue from '@vitejs/plugin-vue'
+
+import path from 'path'
+
+import AutoImport from 'unplugin-auto-import/vite'
+import Components from 'unplugin-vue-components/vite'
+import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
+
+import Icons from 'unplugin-icons/vite'
+import IconsResolver from 'unplugin-icons/resolver'
+
+import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
+
+import { viteMockServe } from 'vite-plugin-mock'
+// 引入 fs 模块
+// import fs from 'fs'
+// const fs = require('fs');
+// https://vitejs.dev/config/
+export default defineConfig(({ command, mode }) => {
+ const env = loadEnv(mode, process.cwd())
+
+ return {
+ plugins: [
+ vue(),
+
+ AutoImport({
+ resolvers: [
+ ElementPlusResolver(),
+ IconsResolver({
+ prefix: 'Icon',
+ }),
+ ],
+ }),
+ Components({
+ resolvers: [
+ ElementPlusResolver({importStyle:"sass"}),
+ IconsResolver({
+ enabledCollections: ['ep'],
+ }),
+ ],
+ }),
+ Icons({
+ autoInstall: true,
+ }),
+ createSvgIconsPlugin({
+ // Specify the icon folder to be cached
+ iconDirs: [path.resolve(process.cwd(), 'src/assets/icons/default'), path.resolve(process.cwd(), 'src/assets/icons/bosch')],
+ // Specify symbolId format
+ symbolId: 'icon-[dir]-[name]',
+ }),
+ viteMockServe({
+ localEnabled: command === 'serve',
+ }),
+ ],
+ resolve: {
+ alias: {
+ '@': path.resolve('./src'), // 相对路径别名配置,使用 @ 代替 src
+ },
+ },
+ css: { //配置全局scss
+ preprocessorOptions: {
+ scss: {
+ quietDeps: true,
+ logger: {
+ warn: () => {}
+ },
+ javascriptEnabled: true,
+ // additionalData: `@use "@/styles/element/index.scss" as *;`,
+ },
+ },
+ },
+ // 代理跨域
+ server: {
+ // https: {
+ // key: fs.readFileSync('./cert.key'), // 证书密钥路径
+ // cert: fs.readFileSync('./cert.crt') // 证书文件路径
+ // },
+ host: '0.0.0.0',
+ proxy: {
+ [env.VITE_APP_BASE_API]: {
+ target: env.VITE_SERVE,
+ changeOrigin: true,
+ rewrite: (path) =>
+ path.replace(new RegExp(`^${env.VITE_APP_BASE_API}`), ''),
+ },
+ },
+ },
+ }
+})