重命名前端目录
@@ -163,8 +163,9 @@ bash deploy/ubuntu-deploy.sh
|
|||||||
- `apps/backend/src/services/fulfillment`:快手 Cloud 履约编排
|
- `apps/backend/src/services/fulfillment`:快手 Cloud 履约编排
|
||||||
- `apps/backend/src/services/admin`:后台读写、权限、配置管理
|
- `apps/backend/src/services/admin`:后台读写、权限、配置管理
|
||||||
- `apps/backend/src/repositories`:数据库访问
|
- `apps/backend/src/repositories`:数据库访问
|
||||||
- `apps/frontend/src/views/admin`:后台页面
|
- `apps/frontend/src/pages/admin`:React 后台页面
|
||||||
- `apps/frontend/src/views/claim`:用户领取页
|
- `apps/frontend/src/pages/claim`:React 用户领取页
|
||||||
|
- `apps/frontend-vue/src/views`:旧 Vue 前端保留目录
|
||||||
|
|
||||||
## 提交前检查清单
|
## 提交前检查清单
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "order-site-frontend-react",
|
|
||||||
"private": true,
|
|
||||||
"version": "0.1.0",
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "vite",
|
|
||||||
"build": "tsc -b && vite build",
|
|
||||||
"typecheck": "tsc -b --noEmit",
|
|
||||||
"preview": "vite preview"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@ant-design/icons": "^6.1.0",
|
|
||||||
"@tanstack/react-query": "^5.90.12",
|
|
||||||
"antd": "^6.1.1",
|
|
||||||
"axios": "^1.13.2",
|
|
||||||
"dayjs": "^1.11.21",
|
|
||||||
"qrcode": "^1.5.4",
|
|
||||||
"react": "^19.2.3",
|
|
||||||
"react-dom": "^19.2.3",
|
|
||||||
"react-router": "^7.10.1"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/node": "^25.6.0",
|
|
||||||
"@types/qrcode": "^1.5.6",
|
|
||||||
"@types/react": "^19.2.7",
|
|
||||||
"@types/react-dom": "^19.2.3",
|
|
||||||
"@vitejs/plugin-react": "^6.0.1",
|
|
||||||
"typescript": "~6.0.2",
|
|
||||||
"vite": "^8.0.7"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
import { Input, Modal, message as antdMessage } from 'antd'
|
|
||||||
import type { ModalFuncProps } from 'antd'
|
|
||||||
import { createElement } from 'react'
|
|
||||||
|
|
||||||
type MessageOptions = {
|
|
||||||
duration?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
type PromptResult = {
|
|
||||||
value: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function showSuccess(message: string, options: MessageOptions = {}) {
|
|
||||||
return antdMessage.success({
|
|
||||||
content: message,
|
|
||||||
duration: options.duration,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function showError(message: string, options: MessageOptions = {}) {
|
|
||||||
return antdMessage.error({
|
|
||||||
content: message,
|
|
||||||
duration: options.duration,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function showConfirm(
|
|
||||||
message: string,
|
|
||||||
title = '确认操作',
|
|
||||||
options: ModalFuncProps = {},
|
|
||||||
) {
|
|
||||||
return new Promise<void>((resolve, reject) => {
|
|
||||||
Modal.confirm({
|
|
||||||
title,
|
|
||||||
content: message,
|
|
||||||
okText: '继续执行',
|
|
||||||
cancelText: '取消',
|
|
||||||
centered: true,
|
|
||||||
...options,
|
|
||||||
onOk: () => {
|
|
||||||
options.onOk?.()
|
|
||||||
resolve()
|
|
||||||
},
|
|
||||||
onCancel: () => {
|
|
||||||
options.onCancel?.()
|
|
||||||
reject('cancel')
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function showPrompt(message: string, title: string, options: ModalFuncProps = {}) {
|
|
||||||
let value = ''
|
|
||||||
|
|
||||||
return new Promise<PromptResult>((resolve, reject) => {
|
|
||||||
Modal.confirm({
|
|
||||||
title,
|
|
||||||
content: createElement('div', { className: 'feedback-prompt' }, [
|
|
||||||
createElement('p', { key: 'message' }, message),
|
|
||||||
createElement(Input, {
|
|
||||||
key: 'input',
|
|
||||||
autoFocus: true,
|
|
||||||
onChange: (event) => {
|
|
||||||
value = event.target.value
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
okText: '提交',
|
|
||||||
cancelText: '取消',
|
|
||||||
centered: true,
|
|
||||||
...options,
|
|
||||||
onOk: () => {
|
|
||||||
options.onOk?.()
|
|
||||||
resolve({ value })
|
|
||||||
},
|
|
||||||
onCancel: () => {
|
|
||||||
options.onCancel?.()
|
|
||||||
reject('cancel')
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isFeedbackDismissed(error: unknown) {
|
|
||||||
return error === 'cancel' || error === 'close'
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
||||||
"target": "ES2022",
|
|
||||||
"useDefineForClassFields": true,
|
|
||||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
||||||
"allowJs": false,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"strict": true,
|
|
||||||
"ignoreDeprecations": "6.0",
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleResolution": "Bundler",
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"jsx": "react-jsx",
|
|
||||||
"baseUrl": ".",
|
|
||||||
"paths": {
|
|
||||||
"@/*": ["src/*"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"include": ["src"]
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"files": [],
|
|
||||||
"references": [
|
|
||||||
{ "path": "./tsconfig.app.json" },
|
|
||||||
{ "path": "./tsconfig.node.json" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
||||||
"target": "ES2023",
|
|
||||||
"lib": ["ES2023"],
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleResolution": "Bundler",
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"strict": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"types": ["node"]
|
|
||||||
},
|
|
||||||
"include": ["vite.config.ts"]
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import path from 'node:path'
|
|
||||||
import { fileURLToPath } from 'node:url'
|
|
||||||
|
|
||||||
import react from '@vitejs/plugin-react'
|
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
|
|
||||||
const currentDir = path.dirname(fileURLToPath(import.meta.url))
|
|
||||||
const apiTarget = process.env.VITE_API_TARGET || 'http://127.0.0.1'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [react()],
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
'@': path.resolve(currentDir, 'src'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
server: {
|
|
||||||
host: '0.0.0.0',
|
|
||||||
port: 5174,
|
|
||||||
proxy: {
|
|
||||||
'/api': {
|
|
||||||
target: apiTarget,
|
|
||||||
changeOrigin: true,
|
|
||||||
},
|
|
||||||
'/health': {
|
|
||||||
target: apiTarget,
|
|
||||||
changeOrigin: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -2,11 +2,12 @@
|
|||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>order-site react</title>
|
<title>自动兑换前端</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "order-site-rewrite",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vue-tsc -b && vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"typecheck": "vue-tsc --noEmit -p tsconfig.app.json",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"format": "prettier --write .",
|
||||||
|
"format:check": "prettier --check ."
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@types/qrcode": "^1.5.6",
|
||||||
|
"axios": "^1.9.0",
|
||||||
|
"element-plus": "^2.10.2",
|
||||||
|
"qrcode": "^1.5.4",
|
||||||
|
"vue": "^3.5.30",
|
||||||
|
"vue-router": "^4.5.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^10.0.1",
|
||||||
|
"@types/node": "^24.12.0",
|
||||||
|
"@vitejs/plugin-vue": "^6.0.5",
|
||||||
|
"@vue/tsconfig": "^0.9.0",
|
||||||
|
"eslint": "^10.3.0",
|
||||||
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
"eslint-plugin-vue": "^10.9.1",
|
||||||
|
"globals": "^17.6.0",
|
||||||
|
"prettier": "^3.8.3",
|
||||||
|
"sass": "^1.99.0",
|
||||||
|
"typescript": "~5.9.3",
|
||||||
|
"typescript-eslint": "^8.59.3",
|
||||||
|
"unplugin-auto-import": "^21.0.0",
|
||||||
|
"unplugin-vue-components": "^32.0.0",
|
||||||
|
"vite": "^8.0.7",
|
||||||
|
"vue-tsc": "^3.2.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 245 KiB After Width: | Height: | Size: 245 KiB |
|
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 329 KiB After Width: | Height: | Size: 329 KiB |
@@ -0,0 +1,67 @@
|
|||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import type { ElMessageBoxOptions, MessageOptions } from 'element-plus'
|
||||||
|
|
||||||
|
import 'element-plus/theme-chalk/el-message.css'
|
||||||
|
import 'element-plus/theme-chalk/el-overlay.css'
|
||||||
|
import 'element-plus/theme-chalk/el-message-box.css'
|
||||||
|
|
||||||
|
const baseMessageOptions: Partial<MessageOptions> = {
|
||||||
|
grouping: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseConfirmOptions: ElMessageBoxOptions = {
|
||||||
|
type: 'warning',
|
||||||
|
confirmButtonText: '继续执行',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
closeOnClickModal: false,
|
||||||
|
closeOnPressEscape: false,
|
||||||
|
autofocus: false,
|
||||||
|
distinguishCancelAndClose: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
const basePromptOptions: ElMessageBoxOptions = {
|
||||||
|
confirmButtonText: '提交',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
closeOnClickModal: false,
|
||||||
|
closeOnPressEscape: false,
|
||||||
|
autofocus: false,
|
||||||
|
distinguishCancelAndClose: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showSuccess(message: string, options: Partial<MessageOptions> = {}) {
|
||||||
|
return ElMessage.success({
|
||||||
|
...baseMessageOptions,
|
||||||
|
...options,
|
||||||
|
message,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showError(message: string, options: Partial<MessageOptions> = {}) {
|
||||||
|
return ElMessage.error({
|
||||||
|
...baseMessageOptions,
|
||||||
|
...options,
|
||||||
|
message,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showConfirm(
|
||||||
|
message: string,
|
||||||
|
title = '确认操作',
|
||||||
|
options: ElMessageBoxOptions = {},
|
||||||
|
) {
|
||||||
|
return ElMessageBox.confirm(message, title, {
|
||||||
|
...baseConfirmOptions,
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showPrompt(message: string, title: string, options: ElMessageBoxOptions = {}) {
|
||||||
|
return ElMessageBox.prompt(message, title, {
|
||||||
|
...basePromptOptions,
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isFeedbackDismissed(error: unknown) {
|
||||||
|
return error === 'cancel' || error === 'close'
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
:root {
|
||||||
|
color: var(--text-primary);
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at top left, rgba(44, 133, 247, 0.22), transparent 32%),
|
||||||
|
radial-gradient(circle at top right, rgba(114, 184, 255, 0.18), transparent 26%),
|
||||||
|
linear-gradient(180deg, var(--bg-page) 0%, #f7fbff 100%);
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
line-height: 1.5;
|
||||||
|
font-weight: 400;
|
||||||
|
font-synthesis: none;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
#app {
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body::before {
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
background-image:
|
||||||
|
linear-gradient(rgba(20, 47, 95, 0.02) 1px, transparent 1px),
|
||||||
|
linear-gradient(90deg, rgba(20, 47, 95, 0.02) 1px, transparent 1px);
|
||||||
|
background-size: 24px 24px;
|
||||||
|
mask-image: radial-gradient(circle at center, black, transparent 75%);
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||