重命名前端目录
@@ -163,8 +163,9 @@ bash deploy/ubuntu-deploy.sh
|
||||
- `apps/backend/src/services/fulfillment`:快手 Cloud 履约编排
|
||||
- `apps/backend/src/services/admin`:后台读写、权限、配置管理
|
||||
- `apps/backend/src/repositories`:数据库访问
|
||||
- `apps/frontend/src/views/admin`:后台页面
|
||||
- `apps/frontend/src/views/claim`:用户领取页
|
||||
- `apps/frontend/src/pages/admin`:React 后台页面
|
||||
- `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">
|
||||
<head>
|
||||
<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" />
|
||||
<title>order-site react</title>
|
||||
<title>自动兑换前端</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</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;
|
||||
}
|
||||