fix:先提交一版本的续保
This commit is contained in:
12
.editorconfig
Normal file
12
.editorconfig
Normal file
@@ -0,0 +1,12 @@
|
||||
root = true
|
||||
[*.{js,ts,vue}]
|
||||
charset = utf-8 # 设置文件字符集为 utf-8
|
||||
end_of_line = lf # 控制换行类型(lf | cr | crlf)
|
||||
insert_final_newline = true # 始终在文件末尾插入一个新行
|
||||
indent_style = space # 缩进风格(tab | space)
|
||||
indent_size = 2 # 缩进大小
|
||||
max_line_length = 100 # 最大行长度
|
||||
|
||||
[*.md] # 仅 md 文件适用以下规则
|
||||
max_line_length = off # 关闭最大行长度限制
|
||||
trim_trailing_whitespace = false # 关闭末尾空格修剪
|
||||
27
.env
Normal file
27
.env
Normal file
@@ -0,0 +1,27 @@
|
||||
# 标题
|
||||
VITE_APP_TITLE=途安伴旅后台管理
|
||||
|
||||
# 项目本地运行端口号
|
||||
VITE_PORT=80
|
||||
|
||||
# open 运行 npm run dev 时自动打开浏览器
|
||||
VITE_OPEN=true
|
||||
|
||||
# 租户开关
|
||||
VITE_APP_TENANT_ENABLE=true
|
||||
|
||||
# 验证码的开关
|
||||
VITE_APP_CAPTCHA_ENABLE=true
|
||||
|
||||
# 文档地址的开关
|
||||
VITE_APP_DOCALERT_ENABLE=false
|
||||
|
||||
# 百度统计
|
||||
VITE_APP_BAIDU_CODE = a1ff8825baa73c3a78eb96aa40325abc
|
||||
|
||||
# 默认账户密码
|
||||
VITE_APP_DEFAULT_LOGIN_TENANT = 途安伴旅
|
||||
VITE_APP_DEFAULT_LOGIN_USERNAME =
|
||||
VITE_APP_DEFAULT_LOGIN_PASSWORD =
|
||||
#VITE_APP_DEFAULT_LOGIN_USERNAME = admin
|
||||
#VITE_APP_DEFAULT_LOGIN_PASSWORD = admin123
|
||||
36
.env.local
Normal file
36
.env.local
Normal file
@@ -0,0 +1,36 @@
|
||||
# 本地开发环境:本地启动所有项目(前端、后端、APP)时使用,不依赖外部环境
|
||||
NODE_ENV=development
|
||||
|
||||
VITE_DEV=true
|
||||
|
||||
# 请求路径
|
||||
|
||||
#VITE_BASE_URL='http://localhost:48080'
|
||||
VITE_BASE_URL='https://api.tuanbanlv.com'
|
||||
|
||||
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
|
||||
VITE_UPLOAD_TYPE=client
|
||||
|
||||
# 上传路径
|
||||
VITE_UPLOAD_URL='https://api.tuanbanlv.com/admin-api/infra/file/upload'
|
||||
|
||||
# 接口地址
|
||||
VITE_API_URL=/admin-api
|
||||
|
||||
# 是否删除debugger
|
||||
VITE_DROP_DEBUGGER=false
|
||||
|
||||
# 是否删除console.log
|
||||
VITE_DROP_CONSOLE=false
|
||||
|
||||
# 是否sourcemap
|
||||
VITE_SOURCEMAP=false
|
||||
|
||||
# 打包路径
|
||||
VITE_BASE_PATH=/
|
||||
|
||||
# 商城H5会员端域名
|
||||
VITE_MALL_H5_DOMAIN='http://localhost:3000'
|
||||
|
||||
# 验证码的开关
|
||||
VITE_APP_CAPTCHA_ENABLE=true
|
||||
39
.env.prod
Normal file
39
.env.prod
Normal file
@@ -0,0 +1,39 @@
|
||||
# 生产环境:只在打包时使用
|
||||
NODE_ENV=production
|
||||
|
||||
VITE_DEV=false
|
||||
|
||||
# 请求路径
|
||||
VITE_BASE_URL='https://api.tuanbanlv.com'
|
||||
#VITE_BASE_URL='https://zmingzhikeji.cn'
|
||||
|
||||
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
|
||||
VITE_UPLOAD_TYPE=client
|
||||
|
||||
# 上传路径
|
||||
VITE_UPLOAD_URL='https://api.tuanbanlv.com/admin-api/infra/file/upload'
|
||||
#VITE_UPLOAD_URL='https://zmingzhikeji.cn/admin-api/infra/file/upload'
|
||||
|
||||
# 接口地址
|
||||
VITE_API_URL=/admin-api
|
||||
|
||||
# 是否删除debugger
|
||||
VITE_DROP_DEBUGGER=true
|
||||
|
||||
# 是否删除console.log
|
||||
VITE_DROP_CONSOLE=true
|
||||
|
||||
# 是否sourcemap
|
||||
VITE_SOURCEMAP=false
|
||||
|
||||
# 打包路径
|
||||
VITE_BASE_PATH=/
|
||||
|
||||
# 输出路径
|
||||
VITE_OUT_DIR=dist-prod
|
||||
|
||||
# 商城H5会员端域名
|
||||
VITE_MALL_H5_DOMAIN='http://1.14.158.154'
|
||||
|
||||
# 验证码的开关
|
||||
VITE_APP_CAPTCHA_ENABLE=true
|
||||
33
.env.stage
Normal file
33
.env.stage
Normal file
@@ -0,0 +1,33 @@
|
||||
# 预发布环境:只在打包时使用
|
||||
NODE_ENV=production
|
||||
|
||||
VITE_DEV=false
|
||||
|
||||
# 请求路径
|
||||
VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn'
|
||||
|
||||
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
|
||||
VITE_UPLOAD_TYPE=server
|
||||
# 上传路径
|
||||
VITE_UPLOAD_URL='http://api-dashboard.yudao.iocoder.cn/admin-api/infra/file/upload'
|
||||
|
||||
# 接口地址
|
||||
VITE_API_URL=/admin-api
|
||||
|
||||
# 是否删除debugger
|
||||
VITE_DROP_DEBUGGER=true
|
||||
|
||||
# 是否删除console.log
|
||||
VITE_DROP_CONSOLE=true
|
||||
|
||||
# 是否sourcemap
|
||||
VITE_SOURCEMAP=false
|
||||
|
||||
# 打包路径
|
||||
VITE_BASE_PATH='http://static-vue3.yudao.iocoder.cn/'
|
||||
|
||||
# 输出路径
|
||||
VITE_OUT_DIR=dist-stage
|
||||
|
||||
# 商城H5会员端域名
|
||||
VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
|
||||
37
.env.test
Normal file
37
.env.test
Normal file
@@ -0,0 +1,37 @@
|
||||
# 生产环境:只在打包时使用
|
||||
NODE_ENV=production
|
||||
|
||||
VITE_DEV=false
|
||||
|
||||
# 请求路径
|
||||
VITE_BASE_URL='https://test.zmingzhikeji.cn'
|
||||
|
||||
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
|
||||
VITE_UPLOAD_TYPE=client
|
||||
|
||||
# 上传路径
|
||||
VITE_UPLOAD_URL='https://test.zmingzhikeji.cn/admin-api/infra/file/upload'
|
||||
|
||||
# 接口地址
|
||||
VITE_API_URL=/admin-api
|
||||
|
||||
# 是否删除debugger
|
||||
VITE_DROP_DEBUGGER=true
|
||||
|
||||
# 是否删除console.log
|
||||
VITE_DROP_CONSOLE=true
|
||||
|
||||
# 是否sourcemap
|
||||
VITE_SOURCEMAP=false
|
||||
|
||||
# 打包路径
|
||||
VITE_BASE_PATH=/
|
||||
|
||||
# 输出路径
|
||||
VITE_OUT_DIR=dist-test
|
||||
|
||||
# 商城H5会员端域名
|
||||
VITE_MALL_H5_DOMAIN='https://zmingzhikeji.cn'
|
||||
|
||||
# 验证码的开关
|
||||
VITE_APP_CAPTCHA_ENABLE=true
|
||||
8
.eslintignore
Normal file
8
.eslintignore
Normal file
@@ -0,0 +1,8 @@
|
||||
/build/
|
||||
/config/
|
||||
/dist/
|
||||
/*.js
|
||||
/test/unit/coverage/
|
||||
/node_modules/*
|
||||
/dist*
|
||||
/src/main.ts
|
||||
259
.eslintrc-auto-import.json
Normal file
259
.eslintrc-auto-import.json
Normal file
@@ -0,0 +1,259 @@
|
||||
{
|
||||
"globals": {
|
||||
"EffectScope": true,
|
||||
"ElMessage": true,
|
||||
"ElMessageBox": true,
|
||||
"ElTag": true,
|
||||
"asyncComputed": true,
|
||||
"autoResetRef": true,
|
||||
"computed": true,
|
||||
"computedAsync": true,
|
||||
"computedEager": true,
|
||||
"computedInject": true,
|
||||
"computedWithControl": true,
|
||||
"controlledComputed": true,
|
||||
"controlledRef": true,
|
||||
"createApp": true,
|
||||
"createEventHook": true,
|
||||
"createGlobalState": true,
|
||||
"createInjectionState": true,
|
||||
"createReactiveFn": true,
|
||||
"createSharedComposable": true,
|
||||
"createUnrefFn": true,
|
||||
"customRef": true,
|
||||
"debouncedRef": true,
|
||||
"debouncedWatch": true,
|
||||
"defineAsyncComponent": true,
|
||||
"defineComponent": true,
|
||||
"eagerComputed": true,
|
||||
"effectScope": true,
|
||||
"extendRef": true,
|
||||
"getCurrentInstance": true,
|
||||
"getCurrentScope": true,
|
||||
"h": true,
|
||||
"ignorableWatch": true,
|
||||
"inject": true,
|
||||
"isDefined": true,
|
||||
"isProxy": true,
|
||||
"isReactive": true,
|
||||
"isReadonly": true,
|
||||
"isRef": true,
|
||||
"makeDestructurable": true,
|
||||
"markRaw": true,
|
||||
"nextTick": true,
|
||||
"onActivated": true,
|
||||
"onBeforeMount": true,
|
||||
"onBeforeUnmount": true,
|
||||
"onBeforeUpdate": true,
|
||||
"onClickOutside": true,
|
||||
"onDeactivated": true,
|
||||
"onErrorCaptured": true,
|
||||
"onKeyStroke": true,
|
||||
"onLongPress": true,
|
||||
"onMounted": true,
|
||||
"onRenderTracked": true,
|
||||
"onRenderTriggered": true,
|
||||
"onScopeDispose": true,
|
||||
"onServerPrefetch": true,
|
||||
"onStartTyping": true,
|
||||
"onUnmounted": true,
|
||||
"onUpdated": true,
|
||||
"pausableWatch": true,
|
||||
"provide": true,
|
||||
"reactify": true,
|
||||
"reactifyObject": true,
|
||||
"reactive": true,
|
||||
"reactiveComputed": true,
|
||||
"reactiveOmit": true,
|
||||
"reactivePick": true,
|
||||
"readonly": true,
|
||||
"ref": true,
|
||||
"refAutoReset": true,
|
||||
"refDebounced": true,
|
||||
"refDefault": true,
|
||||
"refThrottled": true,
|
||||
"refWithControl": true,
|
||||
"resolveComponent": true,
|
||||
"resolveRef": true,
|
||||
"resolveUnref": true,
|
||||
"shallowReactive": true,
|
||||
"shallowReadonly": true,
|
||||
"shallowRef": true,
|
||||
"syncRef": true,
|
||||
"syncRefs": true,
|
||||
"templateRef": true,
|
||||
"throttledRef": true,
|
||||
"throttledWatch": true,
|
||||
"toRaw": true,
|
||||
"toReactive": true,
|
||||
"toRef": true,
|
||||
"toRefs": true,
|
||||
"triggerRef": true,
|
||||
"tryOnBeforeMount": true,
|
||||
"tryOnBeforeUnmount": true,
|
||||
"tryOnMounted": true,
|
||||
"tryOnScopeDispose": true,
|
||||
"tryOnUnmounted": true,
|
||||
"unref": true,
|
||||
"unrefElement": true,
|
||||
"until": true,
|
||||
"useActiveElement": true,
|
||||
"useArrayEvery": true,
|
||||
"useArrayFilter": true,
|
||||
"useArrayFind": true,
|
||||
"useArrayFindIndex": true,
|
||||
"useArrayJoin": true,
|
||||
"useArrayMap": true,
|
||||
"useArrayReduce": true,
|
||||
"useArraySome": true,
|
||||
"useAsyncQueue": true,
|
||||
"useAsyncState": true,
|
||||
"useAttrs": true,
|
||||
"useBase64": true,
|
||||
"useBattery": true,
|
||||
"useBluetooth": true,
|
||||
"useBreakpoints": true,
|
||||
"useBroadcastChannel": true,
|
||||
"useBrowserLocation": true,
|
||||
"useCached": true,
|
||||
"useClipboard": true,
|
||||
"useColorMode": true,
|
||||
"useConfirmDialog": true,
|
||||
"useCounter": true,
|
||||
"useCssModule": true,
|
||||
"useCssVar": true,
|
||||
"useCssVars": true,
|
||||
"useCurrentElement": true,
|
||||
"useCycleList": true,
|
||||
"useDark": true,
|
||||
"useDateFormat": true,
|
||||
"useDebounce": true,
|
||||
"useDebounceFn": true,
|
||||
"useDebouncedRefHistory": true,
|
||||
"useDeviceMotion": true,
|
||||
"useDeviceOrientation": true,
|
||||
"useDevicePixelRatio": true,
|
||||
"useDevicesList": true,
|
||||
"useDisplayMedia": true,
|
||||
"useDocumentVisibility": true,
|
||||
"useDraggable": true,
|
||||
"useDropZone": true,
|
||||
"useElementBounding": true,
|
||||
"useElementByPoint": true,
|
||||
"useElementHover": true,
|
||||
"useElementSize": true,
|
||||
"useElementVisibility": true,
|
||||
"useEventBus": true,
|
||||
"useEventListener": true,
|
||||
"useEventSource": true,
|
||||
"useEyeDropper": true,
|
||||
"useFavicon": true,
|
||||
"useFetch": true,
|
||||
"useFileDialog": true,
|
||||
"useFileSystemAccess": true,
|
||||
"useFocus": true,
|
||||
"useFocusWithin": true,
|
||||
"useFps": true,
|
||||
"useFullscreen": true,
|
||||
"useGamepad": true,
|
||||
"useGeolocation": true,
|
||||
"useIdle": true,
|
||||
"useImage": true,
|
||||
"useInfiniteScroll": true,
|
||||
"useIntersectionObserver": true,
|
||||
"useInterval": true,
|
||||
"useIntervalFn": true,
|
||||
"useKeyModifier": true,
|
||||
"useLastChanged": true,
|
||||
"useLocalStorage": true,
|
||||
"useMagicKeys": true,
|
||||
"useManualRefHistory": true,
|
||||
"useMediaControls": true,
|
||||
"useMediaQuery": true,
|
||||
"useMemoize": true,
|
||||
"useMemory": true,
|
||||
"useMounted": true,
|
||||
"useMouse": true,
|
||||
"useMouseInElement": true,
|
||||
"useMousePressed": true,
|
||||
"useMutationObserver": true,
|
||||
"useNavigatorLanguage": true,
|
||||
"useNetwork": true,
|
||||
"useNow": true,
|
||||
"useObjectUrl": true,
|
||||
"useOffsetPagination": true,
|
||||
"useOnline": true,
|
||||
"usePageLeave": true,
|
||||
"useParallax": true,
|
||||
"usePermission": true,
|
||||
"usePointer": true,
|
||||
"usePointerSwipe": true,
|
||||
"usePreferredColorScheme": true,
|
||||
"usePreferredDark": true,
|
||||
"usePreferredLanguages": true,
|
||||
"useRafFn": true,
|
||||
"useRefHistory": true,
|
||||
"useResizeObserver": true,
|
||||
"useRoute": true,
|
||||
"useRouter": true,
|
||||
"useScreenOrientation": true,
|
||||
"useScreenSafeArea": true,
|
||||
"useScriptTag": true,
|
||||
"useScroll": true,
|
||||
"useScrollLock": true,
|
||||
"useSessionStorage": true,
|
||||
"useShare": true,
|
||||
"useSlots": true,
|
||||
"useSpeechRecognition": true,
|
||||
"useSpeechSynthesis": true,
|
||||
"useStepper": true,
|
||||
"useStorage": true,
|
||||
"useStorageAsync": true,
|
||||
"useStyleTag": true,
|
||||
"useSupported": true,
|
||||
"useSwipe": true,
|
||||
"useTemplateRefsList": true,
|
||||
"useTextDirection": true,
|
||||
"useTextSelection": true,
|
||||
"useTextareaAutosize": true,
|
||||
"useThrottle": true,
|
||||
"useThrottleFn": true,
|
||||
"useThrottledRefHistory": true,
|
||||
"useTimeAgo": true,
|
||||
"useTimeout": true,
|
||||
"useTimeoutFn": true,
|
||||
"useTimeoutPoll": true,
|
||||
"useTimestamp": true,
|
||||
"useTitle": true,
|
||||
"useToggle": true,
|
||||
"useTransition": true,
|
||||
"useUrlSearchParams": true,
|
||||
"useUserMedia": true,
|
||||
"useVModel": true,
|
||||
"useVModels": true,
|
||||
"useVibrate": true,
|
||||
"useVirtualList": true,
|
||||
"useWakeLock": true,
|
||||
"useWebNotification": true,
|
||||
"useWebSocket": true,
|
||||
"useWebWorker": true,
|
||||
"useWebWorkerFn": true,
|
||||
"useWindowFocus": true,
|
||||
"useWindowScroll": true,
|
||||
"useWindowSize": true,
|
||||
"watch": true,
|
||||
"watchArray": true,
|
||||
"watchAtMost": true,
|
||||
"watchDebounced": true,
|
||||
"watchEffect": true,
|
||||
"watchIgnorable": true,
|
||||
"watchOnce": true,
|
||||
"watchPausable": true,
|
||||
"watchPostEffect": true,
|
||||
"watchSyncEffect": true,
|
||||
"watchThrottled": true,
|
||||
"watchTriggerable": true,
|
||||
"watchWithFilter": true,
|
||||
"whenever": true
|
||||
}
|
||||
}
|
||||
75
.eslintrc.js
Normal file
75
.eslintrc.js
Normal file
@@ -0,0 +1,75 @@
|
||||
// @ts-check
|
||||
const { defineConfig } = require('eslint-define-config')
|
||||
module.exports = defineConfig({
|
||||
root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true
|
||||
},
|
||||
parser: 'vue-eslint-parser',
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser',
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
jsxPragma: 'React',
|
||||
ecmaFeatures: {
|
||||
jsx: true
|
||||
}
|
||||
},
|
||||
extends: [
|
||||
'plugin:vue/vue3-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier',
|
||||
'plugin:prettier/recommended',
|
||||
'@unocss'
|
||||
],
|
||||
rules: {
|
||||
'vue/no-setup-props-destructure': 'off',
|
||||
'vue/script-setup-uses-vars': 'error',
|
||||
'vue/no-reserved-component-names': 'off',
|
||||
'@typescript-eslint/ban-ts-ignore': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'vue/custom-event-name-casing': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'space-before-function-paren': 'off',
|
||||
|
||||
'vue/attributes-order': 'off',
|
||||
'vue/one-component-per-file': 'off',
|
||||
'vue/html-closing-bracket-newline': 'off',
|
||||
'vue/max-attributes-per-line': 'off',
|
||||
'vue/multiline-html-element-content-newline': 'off',
|
||||
'vue/singleline-html-element-content-newline': 'off',
|
||||
'vue/attribute-hyphenation': 'off',
|
||||
'vue/require-default-prop': 'off',
|
||||
'vue/require-explicit-emits': 'off',
|
||||
'vue/require-toggle-inside-transition': 'off',
|
||||
'vue/html-self-closing': [
|
||||
'error',
|
||||
{
|
||||
html: {
|
||||
void: 'always',
|
||||
normal: 'never',
|
||||
component: 'always'
|
||||
},
|
||||
svg: 'always',
|
||||
math: 'always'
|
||||
}
|
||||
],
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'vue/no-v-html': 'off',
|
||||
'prettier/prettier': 'off', // 芋艿:默认关闭 prettier 的 ESLint 校验,因为我们使用的是 IDE 的 Prettier 插件
|
||||
'@unocss/order': 'off', // 芋艿:禁用 unocss 【css】顺序的提示,因为暂时不需要这么严格,警告也有点繁琐
|
||||
'@unocss/order-attributify': 'off' // 芋艿:禁用 unocss 【属性】顺序的提示,因为暂时不需要这么严格,警告也有点繁琐
|
||||
}
|
||||
})
|
||||
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
/dist*
|
||||
pnpm-debug
|
||||
auto-*.d.ts
|
||||
.idea
|
||||
.history
|
||||
11
.prettierignore
Normal file
11
.prettierignore
Normal file
@@ -0,0 +1,11 @@
|
||||
/node_modules/**
|
||||
/dist/
|
||||
/dist*
|
||||
/public/*
|
||||
/docs/*
|
||||
/vite.config.ts
|
||||
/src/types/env.d.ts
|
||||
/src/types/auto-components.d.ts
|
||||
/src/types/auto-imports.d.ts
|
||||
/docs/**/*
|
||||
CHANGELOG
|
||||
6
.stylelintignore
Normal file
6
.stylelintignore
Normal file
@@ -0,0 +1,6 @@
|
||||
/dist/*
|
||||
/public/*
|
||||
public/*
|
||||
/dist*
|
||||
/src/types/env.d.ts
|
||||
/docs/**/*
|
||||
18
.vscode/extensions.json
vendored
Normal file
18
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"christian-kohler.path-intellisense",
|
||||
"vscode-icons-team.vscode-icons",
|
||||
"davidanson.vscode-markdownlint",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"mrmlnc.vscode-less",
|
||||
"lokalise.i18n-ally",
|
||||
"redhat.vscode-yaml",
|
||||
"csstools.postcss",
|
||||
"mikestead.dotenv",
|
||||
"eamodio.gitlens",
|
||||
"antfu.iconify",
|
||||
"antfu.unocss",
|
||||
"Vue.volar"
|
||||
]
|
||||
}
|
||||
16
.vscode/launch.json
vendored
Normal file
16
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "msedge",
|
||||
"request": "launch",
|
||||
"name": "Launch Edge against localhost",
|
||||
"url": "http://localhost",
|
||||
"webRoot": "${workspaceFolder}/src",
|
||||
"sourceMaps": true
|
||||
}
|
||||
]
|
||||
}
|
||||
145
.vscode/settings.json
vendored
Normal file
145
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
{
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"npm.packageManager": "pnpm",
|
||||
"editor.tabSize": 2,
|
||||
"prettier.printWidth": 100, // 超过最大值换行
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"files.eol": "\n",
|
||||
"search.exclude": {
|
||||
"**/node_modules": true,
|
||||
"**/*.log": true,
|
||||
"**/*.log*": true,
|
||||
"**/bower_components": true,
|
||||
"**/dist": true,
|
||||
"**/elehukouben": true,
|
||||
"**/.git": true,
|
||||
"**/.gitignore": true,
|
||||
"**/.svn": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/.idea": true,
|
||||
"**/.vscode": false,
|
||||
"**/yarn.lock": true,
|
||||
"**/tmp": true,
|
||||
"out": true,
|
||||
"dist": true,
|
||||
"node_modules": true,
|
||||
"CHANGELOG.md": true,
|
||||
"examples": true,
|
||||
"res": true,
|
||||
"screenshots": true,
|
||||
"yarn-error.log": true,
|
||||
"**/.yarn": true
|
||||
},
|
||||
"files.exclude": {
|
||||
"**/.cache": true,
|
||||
"**/.editorconfig": true,
|
||||
"**/.eslintcache": true,
|
||||
"**/bower_components": true,
|
||||
"**/.idea": true,
|
||||
"**/tmp": true,
|
||||
"**/.git": true,
|
||||
"**/.svn": true,
|
||||
"**/.hg": true,
|
||||
"**/CVS": true,
|
||||
"**/.DS_Store": true
|
||||
},
|
||||
"files.watcherExclude": {
|
||||
"**/.git/objects/**": true,
|
||||
"**/.git/subtree-cache/**": true,
|
||||
"**/.vscode/**": true,
|
||||
"**/node_modules/**": true,
|
||||
"**/tmp/**": true,
|
||||
"**/bower_components/**": true,
|
||||
"**/dist/**": true,
|
||||
"**/yarn.lock": true
|
||||
},
|
||||
"stylelint.enable": true,
|
||||
"stylelint.validate": ["css", "less", "postcss", "scss", "vue", "sass"],
|
||||
"path-intellisense.mappings": {
|
||||
"@/": "${workspaceRoot}/src"
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
|
||||
},
|
||||
"[html]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[css]": {
|
||||
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
|
||||
},
|
||||
"[less]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[scss]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[markdown]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.fixAll.stylelint": "explicit"
|
||||
},
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
|
||||
},
|
||||
"i18n-ally.localesPaths": ["src/locales"],
|
||||
"i18n-ally.keystyle": "nested",
|
||||
"i18n-ally.sortKeys": true,
|
||||
"i18n-ally.namespace": false,
|
||||
"i18n-ally.enabledParsers": ["ts"],
|
||||
"i18n-ally.sourceLanguage": "en",
|
||||
"i18n-ally.displayLanguage": "zh-CN",
|
||||
"i18n-ally.enabledFrameworks": ["vue", "react"],
|
||||
"cSpell.words": [
|
||||
"brotli",
|
||||
"browserslist",
|
||||
"codemirror",
|
||||
"commitlint",
|
||||
"cropperjs",
|
||||
"echart",
|
||||
"echarts",
|
||||
"esnext",
|
||||
"esno",
|
||||
"iconify",
|
||||
"INTLIFY",
|
||||
"lintstagedrc",
|
||||
"logicflow",
|
||||
"nprogress",
|
||||
"pinia",
|
||||
"pnpm",
|
||||
"qrcode",
|
||||
"sider",
|
||||
"sortablejs",
|
||||
"stylelint",
|
||||
"svgs",
|
||||
"unocss",
|
||||
"unplugin",
|
||||
"unref",
|
||||
"videojs",
|
||||
"VITE",
|
||||
"vitejs",
|
||||
"vueuse",
|
||||
"wangeditor",
|
||||
"xingyu",
|
||||
"yudao",
|
||||
"zxcvbn"
|
||||
],
|
||||
// 控制相关文件嵌套展示
|
||||
"explorer.fileNesting.enabled": true,
|
||||
"explorer.fileNesting.expand": false,
|
||||
"explorer.fileNesting.patterns": {
|
||||
"*.ts": "$(capture).test.ts, $(capture).test.tsx",
|
||||
"*.tsx": "$(capture).test.ts, $(capture).test.tsx",
|
||||
"*.env": "$(capture).env.*",
|
||||
"package.json": "pnpm-lock.yaml,yarn.lock,LICENSE,README*,CHANGELOG*,CNAME,.gitattributes,.eslintrc-auto-import.json,.gitignore,prettier.config.js,stylelint.config.js,commitlint.config.js,.stylelintignore,.prettierignore,.gitpod.yml,.eslintrc.js,.eslintignore"
|
||||
},
|
||||
"terminal.integrated.scrollback": 10000,
|
||||
"nuxt.isNuxtApp": false
|
||||
}
|
||||
Submodule ruoyi-vue-pro updated: 39edcaadf4...3738553724
@@ -1,6 +1,6 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
// 车辆续保订单 VO
|
||||
// 车辆订单 VO
|
||||
export interface RenewalOrderVO {
|
||||
id: number // 主键ID
|
||||
carBrand: string // 汽车品牌
|
||||
@@ -45,34 +45,34 @@ export interface RenewalOrderVO {
|
||||
nameplatePhotoUrl?: string // 车名牌照片
|
||||
}
|
||||
|
||||
// 车辆续保订单 API
|
||||
// 车辆订单 API
|
||||
export const RenewalOrderApi = {
|
||||
// 查询车辆续保订单分页
|
||||
// 查询车辆订单分页
|
||||
getRenewalOrderPage: async (params: any) => {
|
||||
return await request.get({ url: `/car/renewal-order/page`, params })
|
||||
},
|
||||
|
||||
// 查询车辆续保订单详情
|
||||
// 查询车辆订单详情
|
||||
getRenewalOrder: async (id: number) => {
|
||||
return await request.get({ url: `/car/renewal-order/get?id=` + id })
|
||||
},
|
||||
|
||||
// 新增车辆续保订单
|
||||
// 新增车辆订单
|
||||
createRenewalOrder: async (data: RenewalOrderVO) => {
|
||||
return await request.post({ url: `/car/renewal-order/create`, data })
|
||||
},
|
||||
|
||||
// 修改车辆续保订单
|
||||
// 修改车辆订单
|
||||
updateRenewalOrder: async (data: RenewalOrderVO) => {
|
||||
return await request.put({ url: `/car/renewal-order/update`, data })
|
||||
},
|
||||
|
||||
// 删除车辆续保订单
|
||||
// 删除车辆订单
|
||||
deleteRenewalOrder: async (id: number) => {
|
||||
return await request.delete({ url: `/car/renewal-order/delete?id=` + id })
|
||||
},
|
||||
|
||||
// 导出车辆续保订单 Excel
|
||||
// 导出车辆订单 Excel
|
||||
exportRenewalOrder: async (params) => {
|
||||
return await request.download({ url: `/car/renewal-order/export-excel`, params })
|
||||
}
|
||||
|
||||
@@ -262,12 +262,12 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-col v-if="showOriginalWarrantyYears" :span="12">
|
||||
<el-form-item label="产品年限" prop="originalWarrantyYears">
|
||||
<el-input v-model="formData.originalWarrantyYears" placeholder="请输入产品年限(如:3)" />
|
||||
<el-input v-model="formData.originalWarrantyYears" placeholder="选择续保产品后自动带出产品生效年限,可修改" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-col :span="showOriginalWarrantyYears ? 12 : 24">
|
||||
<el-form-item label="原厂质保里程" prop="originalWarrantyMileage">
|
||||
<el-input v-model="formData.originalWarrantyMileage" placeholder="请输入原厂质保里程" />
|
||||
</el-form-item>
|
||||
@@ -429,7 +429,7 @@ import { Qrcode } from '@/components/Qrcode'
|
||||
import * as FileApi from '@/api/infra/file'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
|
||||
/** 车辆续保订单 表单 */
|
||||
/** 车辆订单管理 表单 */
|
||||
defineOptions({ name: 'RenewalOrderForm' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
@@ -467,6 +467,8 @@ const productTypeLabel = computed(() => {
|
||||
const showContractComponents = computed(() => {
|
||||
return formData.value.productType === '00' || formData.value.productType === '02'
|
||||
})
|
||||
// 产品年限:仅当产品类别为 00、02(与合同一致的规定品类)时展示并校验
|
||||
const showOriginalWarrantyYears = computed(() => showContractComponents.value)
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
carBrand: undefined,
|
||||
@@ -491,7 +493,7 @@ const formData = ref({
|
||||
serviceProduct: undefined,
|
||||
productType: undefined,
|
||||
productValidity: undefined,
|
||||
originalWarrantyYears: '3',
|
||||
originalWarrantyYears: undefined,
|
||||
originalWarrantyMileage: undefined,
|
||||
productFee: undefined,
|
||||
settlementMethod: '00',
|
||||
@@ -529,7 +531,20 @@ const formRules = reactive({
|
||||
productFee: [{ required: true, message: '产品费用不能为空', trigger: 'blur' }],
|
||||
settlementMethod: [{ required: true, message: '结算方式不能为空', trigger: 'change' }],
|
||||
inputUser: [{ required: true, message: '录单人不能为空', trigger: 'blur' }],
|
||||
originalWarrantyYears: [{ required: true, message: '产品年限不能为空', trigger: 'blur' }],
|
||||
originalWarrantyYears: [
|
||||
{
|
||||
validator: (_rule: any, value: any, callback: (e?: Error) => void) => {
|
||||
const pt = formData.value.productType
|
||||
const need = pt === '00' || pt === '02'
|
||||
if (need && (value == null || String(value).trim() === '')) {
|
||||
callback(new Error('产品年限不能为空'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
drivingLicenseUrl: [{ required: true, message: '请上传行驶证', trigger: 'change' }],
|
||||
carInvoiceUrls: [{
|
||||
required: true,
|
||||
@@ -580,6 +595,7 @@ const handleProductChange = async (productId: number) => {
|
||||
formData.value.serviceProduct = undefined
|
||||
formData.value.productType = undefined
|
||||
formData.value.productValidity = undefined
|
||||
formData.value.originalWarrantyYears = undefined
|
||||
return
|
||||
}
|
||||
try {
|
||||
@@ -592,6 +608,16 @@ const handleProductChange = async (productId: number) => {
|
||||
const p: any = product as any
|
||||
formData.value.productType = p.productType || (p.data && p.data.productType) || ''
|
||||
formData.value.productValidity = product.productContent || ''
|
||||
const pt = formData.value.productType
|
||||
const needWarrantyYears = pt === '00' || pt === '02'
|
||||
if (needWarrantyYears) {
|
||||
const eff = p.effectiveYear ?? p.data?.effectiveYear
|
||||
if (eff != null && eff !== '') {
|
||||
formData.value.originalWarrantyYears = String(eff)
|
||||
}
|
||||
} else {
|
||||
formData.value.originalWarrantyYears = undefined
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取产品详情失败:', error)
|
||||
message.error('获取产品详情失败')
|
||||
@@ -781,19 +807,22 @@ const submitForm = async () => {
|
||||
await formRef.value.validate()
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as RenewalOrderVO
|
||||
const payload = { ...formData.value } as unknown as RenewalOrderVO
|
||||
if (payload.productType !== '00' && payload.productType !== '02') {
|
||||
payload.originalWarrantyYears = undefined as any
|
||||
}
|
||||
if (formType.value === 'create') {
|
||||
const res = await RenewalOrderApi.createRenewalOrder(data) as any
|
||||
const res = await RenewalOrderApi.createRenewalOrder(payload) as any
|
||||
const newId = res?.data ?? res
|
||||
if (newId) formData.value.id = newId
|
||||
if ((data.productType === '00' || data.productType === '02') && newId) {
|
||||
if ((payload.productType === '00' || payload.productType === '02') && newId) {
|
||||
message.success('订单已创建,请让客户扫码完成签名后自动生成合同')
|
||||
await openOnlineSignForOrder(newId)
|
||||
return
|
||||
}
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await RenewalOrderApi.updateRenewalOrder(data)
|
||||
await RenewalOrderApi.updateRenewalOrder(payload)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
@@ -882,7 +911,7 @@ const resetForm = () => {
|
||||
serviceProduct: undefined,
|
||||
productType: undefined,
|
||||
productValidity: undefined,
|
||||
originalWarrantyYears: '3',
|
||||
originalWarrantyYears: undefined,
|
||||
originalWarrantyMileage: undefined,
|
||||
productFee: undefined,
|
||||
settlementMethod: '00',
|
||||
|
||||
@@ -104,7 +104,7 @@ import StoreAnalysis from './components/StoreAnalysis.vue'
|
||||
import SalesTrendChart from './components/SalesTrendChart.vue'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
/** 车辆续保订单看板 */
|
||||
/** 车辆订单管理看板 */
|
||||
defineOptions({ name: 'RenewalOrderDashboard' })
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
@@ -351,19 +351,19 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
|
||||
import { erpPriceTableColumnFormatter } from '@/utils'
|
||||
import { dateFormatter, formatDate } from '@/utils/formatTime'
|
||||
import { erpNumberFormatter, erpPriceTableColumnFormatter } from '@/utils'
|
||||
import download from '@/utils/download'
|
||||
import { RenewalOrderApi, RenewalOrderVO } from '@/api/car/renewalorder'
|
||||
import * as FileApi from '@/api/infra/file'
|
||||
import RenewalOrderForm from './RenewalOrderForm.vue'
|
||||
import TireStoreTree from '@/views/tire/tireuser/TireStoreTree.vue'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { DICT_TYPE, getDictLabel } from '@/utils/dict'
|
||||
import { Qrcode } from '@/components/Qrcode'
|
||||
import JSZip from 'jszip'
|
||||
import request from '@/config/axios'
|
||||
|
||||
/** 车辆续保订单 列表 */
|
||||
/** 车辆订单管理 列表 */
|
||||
defineOptions({ name: 'RenewalOrder' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
@@ -588,16 +588,89 @@ const handleDelete = async (id: number) => {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
/** 按当前筛选条件分页拉取全部订单(与列表查询一致,用于导出) */
|
||||
const fetchAllOrdersForExport = async (): Promise<RenewalOrderVO[]> => {
|
||||
const pageSize = 100
|
||||
const base = { ...queryParams, pageSize }
|
||||
const first = await RenewalOrderApi.getRenewalOrderPage({ ...base, pageNo: 1 })
|
||||
const total = first.total ?? 0
|
||||
if (total === 0) return []
|
||||
const all: RenewalOrderVO[] = [...(first.list || [])]
|
||||
const totalPages = Math.ceil(total / pageSize)
|
||||
for (let p = 2; p <= totalPages; p++) {
|
||||
const data = await RenewalOrderApi.getRenewalOrderPage({ ...base, pageNo: p })
|
||||
all.push(...(data.list || []))
|
||||
}
|
||||
return all
|
||||
}
|
||||
|
||||
/** 将列表数据导出为与表格列一致的 Excel(UTF-8 CSV,Excel 可直接打开) */
|
||||
const exportRenewalOrderListAsExcel = (rows: RenewalOrderVO[]) => {
|
||||
const csvEscape = (s: string) => `"${String(s ?? '').replace(/"/g, '""')}"`
|
||||
const headers = [
|
||||
'序号',
|
||||
'车辆购买方',
|
||||
'车牌号',
|
||||
'车型',
|
||||
'发票金额',
|
||||
'车架号',
|
||||
'服务产品',
|
||||
'产品时效',
|
||||
'产品年限',
|
||||
'产品费用',
|
||||
'结算方式',
|
||||
'录单人',
|
||||
'创建时间',
|
||||
'合同备注'
|
||||
]
|
||||
const lines: string[] = [headers.map(csvEscape).join(',')]
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const row = rows[i]
|
||||
const settlement =
|
||||
getDictLabel(DICT_TYPE.CAR_RENEWAL_PAY_METHOD, row.settlementMethod) ||
|
||||
(row.settlementMethod != null ? String(row.settlementMethod) : '')
|
||||
const line = [
|
||||
String(i + 1),
|
||||
row.carBuyer ?? '',
|
||||
row.licensePlate ?? '',
|
||||
row.carModel ?? '',
|
||||
erpNumberFormatter(row.invoiceAmount, 2),
|
||||
row.vin ?? '',
|
||||
row.serviceProduct ?? '',
|
||||
row.productValidity ?? '',
|
||||
row.originalWarrantyYears != null ? String(row.originalWarrantyYears) : '',
|
||||
erpNumberFormatter(row.productFee, 2),
|
||||
settlement,
|
||||
row.inputUser ?? '',
|
||||
row.createTime ? formatDate(row.createTime) : '',
|
||||
row.contractRemark ?? ''
|
||||
]
|
||||
.map((c) => csvEscape(String(c)))
|
||||
.join(',')
|
||||
lines.push(line)
|
||||
}
|
||||
const csv = '\uFEFF' + lines.join('\r\n')
|
||||
const blob = new Blob([csv], { type: 'application/vnd.ms-excel;charset=utf-8' })
|
||||
download.excel(blob, '车辆订单管理.xls')
|
||||
}
|
||||
|
||||
/** 导出按钮操作:导出当前列表可见字段,数据范围与筛选条件一致(非仅当前页) */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await RenewalOrderApi.exportRenewalOrder(queryParams)
|
||||
download.excel(data, '车辆续保订单.xls')
|
||||
} catch {
|
||||
const rows = await fetchAllOrdersForExport()
|
||||
if (rows.length === 0) {
|
||||
message.warning('当前筛选条件下没有可导出的数据')
|
||||
return
|
||||
}
|
||||
exportRenewalOrderListAsExcel(rows)
|
||||
message.success('导出成功')
|
||||
} catch (e) {
|
||||
if (e !== 'cancel') {
|
||||
console.error(e)
|
||||
message.error('导出失败')
|
||||
}
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
|
||||
291
系统功能梳理.md
Normal file
291
系统功能梳理.md
Normal file
@@ -0,0 +1,291 @@
|
||||
# 途安保单业务管理平台 · 业务梳理
|
||||
|
||||
## 一、业务定位
|
||||
|
||||
**途安保单业务管理平台** 面向汽车延保/无忧保单的销售与管理,支撑保单录入、识别、统计及合同签署等全流程业务。支持 PC 管理后台与移动端(小程序/H5)双端操作。
|
||||
|
||||
---
|
||||
|
||||
## 二、核心业务模块
|
||||
|
||||
### 2.1 保单订单
|
||||
|
||||
**业务说明**:管理途安保单(汽车延保/无忧产品)的销售订单,覆盖从录入到合同签署的全过程。
|
||||
|
||||
| 业务功能 | 业务说明 |
|
||||
|----------|----------|
|
||||
| 订单列表 | 分页查询、按时间/门店/状态等筛选、导出 Excel |
|
||||
| 普通录入 | 业务员手动填写保单信息 |
|
||||
| 图片识别录入 | 上传身份证或机动车销售发票,OCR 识别后自动预填,减少手工录入 |
|
||||
| 订单编辑/删除 | 对已录入订单进行修改或删除 |
|
||||
| 合同生成 | 根据订单信息生成 PDF 合同 |
|
||||
| 在线签名 | 客户扫码签名或现场签名,支持合同签署 |
|
||||
|
||||
**业务对象**:
|
||||
|
||||
| 对象 | 说明 |
|
||||
|------|------|
|
||||
| 车辆信息 | 品牌、车型、车牌、厂牌型号、车架号、发动机号、发票日期、发票金额、购买时公里数 |
|
||||
| 购买方信息 | 服务购买方、车辆购买方、证件类型、证件号、联系电话、联系地址、会员邮箱 |
|
||||
| 产品信息 | 续保产品、产品类别、产品时效、原厂质保时长/里程、产品费用、结算方式 |
|
||||
| 门店信息 | 所属门店、录单人 |
|
||||
| 附件信息 | 身份证正反面、购车发票、行驶证、商业险保单、合格证、里程表照片等 |
|
||||
|
||||
---
|
||||
|
||||
### 2.2 保单产品
|
||||
|
||||
**业务说明**:管理可售的途安保单产品(无忧、延保等),供订单录入时选择。
|
||||
|
||||
| 业务功能 | 业务说明 |
|
||||
|----------|----------|
|
||||
| 产品管理 | 产品名称、产品内容、产品类别、生效年限、备注 |
|
||||
| 产品类别 | 无忧、延保等(按业务字典配置) |
|
||||
| 按销售类别查询 | 根据当前用户销售类别筛选可见产品 |
|
||||
|
||||
---
|
||||
|
||||
### 2.3 销售统计
|
||||
|
||||
**业务说明**:对保单销售进行统计与分析,支撑经营决策和考核。
|
||||
|
||||
| 业务功能 | 业务说明 |
|
||||
|----------|----------|
|
||||
| 汇总统计 | 总订单金额、总订单数、平均订单金额、活跃门店数 |
|
||||
| 门店销售统计 | 各门店销售金额、订单数、平均订单金额、最近开单天数、30 天开单率 |
|
||||
| 业务员销售统计 | 业务员销售金额、订单数、排行、开单率 |
|
||||
| 销售趋势 | 门店/业务员销售趋势图(按时间维度) |
|
||||
| 开单率分析 | 门店/业务员开单率分析 |
|
||||
|
||||
---
|
||||
|
||||
### 2.4 门店管理
|
||||
|
||||
**业务说明**:管理销售门店,订单归属门店,统计按门店维度汇总。
|
||||
|
||||
| 业务功能 | 业务说明 |
|
||||
|----------|----------|
|
||||
| 门店管理 | 门店增删改查 |
|
||||
| 门店选择 | 订单录入时选择所属门店 |
|
||||
|
||||
---
|
||||
|
||||
### 2.5 门店用户
|
||||
|
||||
**业务说明**:门店与业务员(用户)的关联关系,用于权限与数据归属。
|
||||
|
||||
---
|
||||
|
||||
### 2.6 续保与保单到期提醒(第二版)
|
||||
|
||||
**业务说明**:基于首次录入的保险信息,管理续保信息并实现临期提醒与续保开单。
|
||||
|
||||
| 业务功能 | 业务说明 |
|
||||
|----------|----------|
|
||||
| 续保信息录入 | 商业险到期日期、续保公司、日期、车损险保障金额,增删查改 |
|
||||
| 临期续保提醒 | 查询临期保单、提醒业务员跟进 |
|
||||
| 续保开单 | 直接开第二张续保单,与原保单关联 |
|
||||
| 定时刷新 | 定时任务刷新订单到期状态 |
|
||||
|
||||
**业务规则**:续保单与原保单需建立关联关系,便于追溯与统计。
|
||||
|
||||
---
|
||||
|
||||
## 三、业务流程
|
||||
|
||||
### 3.1 保单录入流程
|
||||
|
||||
```
|
||||
1. 选择录入方式
|
||||
├── 普通录入 → 手动填写表单
|
||||
└── 识别录入 → 上传身份证/机动车发票 → 系统识别 → 自动预填 → 补充信息
|
||||
|
||||
2. 填写/补充信息
|
||||
├── 车辆信息
|
||||
├── 购买方信息
|
||||
├── 产品信息(选择续保产品)
|
||||
└── 其他信息(备注、合同备注等)
|
||||
|
||||
3. 提交订单
|
||||
|
||||
4. (可选)生成合同 → 客户扫码/现场签名
|
||||
```
|
||||
|
||||
### 3.2 图片识别流程
|
||||
|
||||
```
|
||||
身份证识别:
|
||||
上传正面 → 识别姓名、证件号等 → 上传反面(可选)→ 识别地址等 → 预填至表单
|
||||
|
||||
机动车销售发票识别:
|
||||
上传发票图片 → 识别品牌、车型、车架号、发动机号、发票金额、购买方等 → 预填至表单
|
||||
```
|
||||
|
||||
### 3.3 合同签署流程
|
||||
|
||||
```
|
||||
1. 选择订单 → 生成合同 PDF
|
||||
2. 创建签名令牌 → 生成签名链接/二维码
|
||||
3. 客户扫码/打开链接 → 在线签名
|
||||
4. 签名完成 → 合同与签名关联保存
|
||||
```
|
||||
|
||||
### 3.4 审批流程(第二版)
|
||||
|
||||
```
|
||||
业务员录入 → 待提交 → 客服审核
|
||||
├── 通过 → 已审核 → 客户签名 → 生成合同
|
||||
└── 驳回 → 已驳回 → 业务员修改 → 再次提交
|
||||
```
|
||||
|
||||
### 3.5 续保流程(第二版)
|
||||
|
||||
```
|
||||
原保单(已到期)→ 点击续保 → 生成新续保单(关联原单)→ 录入续保信息 → 提交
|
||||
```
|
||||
|
||||
### 3.6 分期还款流程(第二版)
|
||||
|
||||
```
|
||||
订单分期 → 设置期数、还款日 → 定时提醒业务员/客户 → 录入还款记录(金额、方式)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、业务规则
|
||||
|
||||
### 4.1 通用规则
|
||||
|
||||
| 规则 | 说明 |
|
||||
|------|------|
|
||||
| 产品可见性 | 业务员仅可见其销售类别下的产品 |
|
||||
| 门店归属 | 订单必须归属门店,统计按门店维度 |
|
||||
| 识别失败 | 识别失败时提示原因,支持重新上传 |
|
||||
| 合同生成 | 可清空已生成合同与签名重新生成 |
|
||||
|
||||
### 4.2 订单状态(第二版)
|
||||
|
||||
| 状态 | 说明 |
|
||||
|------|------|
|
||||
| 草稿 | 未保存完成,可编辑 |
|
||||
| 待保存 | 已填写待保存 |
|
||||
| 待提交 | 已保存待提交审核 |
|
||||
| 待审核 | 已提交,等待客服审核 |
|
||||
| 已审核 | 审核通过,可生成合同、客户签名 |
|
||||
| 已驳回 | 审核失败,业务员重新修改并再次提交 |
|
||||
| 已到期 | 保单到期,可点击续保生成新单据 |
|
||||
|
||||
### 4.3 操作权限(第二版)
|
||||
|
||||
| 角色 | 可操作 |
|
||||
|------|--------|
|
||||
| 普通业务员 | 草稿、待提交未审核:可取消、重新编辑;已到期:可续保开新单;其他状态不可操作 |
|
||||
| 客服 | 续保订单模块审核权限 |
|
||||
|
||||
---
|
||||
|
||||
## 五、支撑业务(非途安保单核心)
|
||||
|
||||
| 模块 | 功能 |
|
||||
|------|------|
|
||||
| 系统管理 | 用户、角色、菜单、部门、字典、租户、通知、日志等 |
|
||||
| 基础设施 | 文件上传、定时任务、代码生成等 |
|
||||
| 食堂 | 其他业务(用户、请假、报表) |
|
||||
|
||||
---
|
||||
|
||||
## 六、途安伴旅出行小程序第二版需求
|
||||
|
||||
> 以下需求来源于《途安伴旅出行小程序第二版功能》文档,按模块与状态分类。
|
||||
|
||||
### 6.1 已完成功能
|
||||
|
||||
| 功能 | 说明 |
|
||||
|------|------|
|
||||
| 客户材料 | 客服可单独查看、批量打包下载保存 |
|
||||
| 远程电子签名 | 通过二维码发给客户签名 |
|
||||
| 合同生成 | 五年期无忧产品合同、两版合同的自动生成 |
|
||||
| 身份证、发票自动识别 | OCR 识别预填 |
|
||||
|
||||
---
|
||||
|
||||
### 6.2 退单处理(待开发)
|
||||
|
||||
| 功能 | 说明 | 预估 |
|
||||
|------|------|------|
|
||||
| 退单录入 | 业务员录入退单信息、退单日期、标记 | 1 天 |
|
||||
| 退单记录查询 | 退单记录列表、筛选、查询 |
|
||||
|
||||
---
|
||||
|
||||
### 6.3 售后板块 · 理赔(待开发)
|
||||
|
||||
| 功能 | 说明 | 预估 |
|
||||
|------|------|------|
|
||||
| 理赔材料 | 理赔材料录入、保存、下载 | 2 天 |
|
||||
| 理赔记录 | 客户理赔记录录入(对客理赔金额、事故定损比例、事故赔偿金额),增删查改,与订单模块交互方式相同 | |
|
||||
| 理赔流程 | 补全理赔资料、理赔审批 |
|
||||
| 角色 | 售后订单录入模块,审核通过后客户签名并生成合同;审核失败则业务员重新修改并再次提交 |
|
||||
|
||||
---
|
||||
|
||||
### 6.4 续保信息录入 + 保单到期提醒 + 续保功能(待开发)
|
||||
|
||||
| 功能 | 说明 | 预估 |
|
||||
|------|------|------|
|
||||
| 续保信息录入 | 根据首次录入的保险信息,录入商业险到期日期、续保公司、日期、车损险保障金额,增删查改 | 2 天 |
|
||||
| 临期续保模块 | 提供临期续保提醒,支持查询和续保操作 |
|
||||
| 续保操作 | 直接开第二张续保单,每张单据需关联原保单 |
|
||||
| 定时任务 | 定时刷新到期状态 |
|
||||
|
||||
**业务规则**:续保提醒基于商业险到期日期,临期提醒支持查询与续保开单。
|
||||
|
||||
---
|
||||
|
||||
### 6.5 分期板块(待开发)
|
||||
|
||||
| 功能 | 说明 | 预估 |
|
||||
|------|------|------|
|
||||
| 分期字段 | 分期首付金额、分期期数(2/3/6/12 期)、还款日 | 2 天 |
|
||||
| 业务员侧 | 还款提醒、查询还款、还款记录标记(还款金额、还款方式:微信/支付宝/转账) | 2 天 |
|
||||
| 客户侧 | 还款短信提醒和告知(定时任务 + 短信) | |
|
||||
| 提醒记录 | 记录模块,记录提醒与还款操作 |
|
||||
| 续保单关联 | 续保单增加还款分期 + 提醒功能 |
|
||||
|
||||
---
|
||||
|
||||
### 6.6 客服审批流程(待开发)
|
||||
|
||||
| 功能 | 说明 | 预估 |
|
||||
|------|------|------|
|
||||
| 审批范围 | 订单录入、售后订单录入模块,审核通过后客户签名并生成合同;审核失败则业务员重新修改并再次提交 | 2 天 |
|
||||
| 客服角色 | 新增客服角色,权限为续保订单模块 |
|
||||
| 订单状态 | 草稿、待保存、待提交、待审核、已审核、已驳回、已到期 |
|
||||
| 定时任务 | 定时刷新到期状态 |
|
||||
|
||||
---
|
||||
|
||||
### 6.7 操作权限管理(待开发)
|
||||
|
||||
| 功能 | 说明 |
|
||||
|------|------|
|
||||
| 普通业务员 | 仅对「草稿」「已提交但未审核」可取消、重新编辑;对「已到期」可点击续保并生成新单据;其他状态不可操作 |
|
||||
| 已生成订单 | 不可删除、不可修改(按状态限制) |
|
||||
|
||||
---
|
||||
|
||||
### 6.8 其他
|
||||
|
||||
| 功能 | 说明 |
|
||||
|------|------|
|
||||
| 第一版细节调整 | 非新增功能,页面排版与交互优化 |
|
||||
|
||||
---
|
||||
|
||||
## 七、业务总结
|
||||
|
||||
- **核心**:途安保单(汽车延保/无忧)订单的录入、管理、统计与合同签署
|
||||
- **录入方式**:普通录入 + 图片识别(身份证、机动车发票)
|
||||
- **统计维度**:汇总、门店、业务员、趋势、开单率
|
||||
- **合同**:PDF 生成 + 在线签名
|
||||
- **第二版重点**:退单、续保与到期提醒、分期、理赔、审批流程、权限
|
||||
@@ -71,7 +71,7 @@ yudao-ui-admin-vue3/
|
||||
- 字典管理、通知公告
|
||||
- 操作日志、登录日志
|
||||
|
||||
#### 2. 车辆续保订单模块(业务相关)
|
||||
#### 2. 车辆订单模块(业务相关)
|
||||
- **API**: `src/api/car/renewalorder/index.ts`
|
||||
- **页面**: `src/views/car/renewalorder/`
|
||||
- `index.vue` - 列表页面
|
||||
@@ -295,7 +295,7 @@ pnpm build:mp
|
||||
|
||||
## 📝 业务模块分析
|
||||
|
||||
### 车辆续保订单模块(PC端)
|
||||
### 车辆订单模块(PC端)
|
||||
|
||||
**位置**: `src/views/car/renewalorder/`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user