feat: init
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
/dist/*
|
||||||
|
.local
|
||||||
|
.output.js
|
||||||
|
.github
|
||||||
|
.vscode
|
||||||
|
/node_modules/**
|
||||||
|
|
||||||
|
*.html
|
||||||
|
**/*.svg
|
||||||
|
**/*.sh
|
||||||
|
|
||||||
|
/public/**
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"printWidth": 160,
|
||||||
|
"arrowParens": "always",
|
||||||
|
"bracketSameLine": true,
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"semi": true,
|
||||||
|
"singleAttributePerLine": false,
|
||||||
|
"vueIndentScriptAndStyle": true,
|
||||||
|
"htmlWhitespaceSensitivity": "ignore"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"recommendations": ["Vue.volar"]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEA2SOM6Heb+BNGha/ucoV+GopddM7ckyGWALhGPJp/Z7P5jgw6
|
||||||
|
NSJ1G/E7CFEukaLC50eupji9mA4o8Emtrgn8y7uMIc5lafHe0IPy+WA90PZyien4
|
||||||
|
0u7dD0NrEbKH41SIEuZFbGev0CgQJsxmkS8CmOytmglyJ0JDBpJD9tQLwSuG9kad
|
||||||
|
DrYPArQdQu+ZALt4gyG7m14c4mZ2hAcIUwNYqFY9g/HIp0q9al8SW3WqzkB1U7GS
|
||||||
|
kh3i/MwtUvZWPI16aKW4/tdUX7y8PPguHQ1MWLd6DI0iGo9tqTQt35o9ax41O+0S
|
||||||
|
EY4MgU1Q3XJaMrmrYUpZ2/Y4xJbHOHgyi6afzQIDAQABAoIBAGms4ovUgkSmZOD1
|
||||||
|
MU/s5eVWx4rsje7RHqa1CAHAkxbOQTq/eqiXX3U83qT6lXZtRvu2KCpfXO4eng/r
|
||||||
|
W6pi0/P3D4j4YOTBwNWsEdkJ3KvQ9QdnpiBJ/a3K+tW/FGEvp5XDGbBbefYNOWcY
|
||||||
|
fSZVQadZMFfSFwtCNUqCbq82nY3hkoFVGiQ9EHUlvNCQM4y5VeJuCPzsl8rzAsyo
|
||||||
|
gpkHyKxU/CNg+f5UuPwostR5eTXgkp6nlpa65yDK0szsww78keE/J1tOB0d4r+Oe
|
||||||
|
12ZVzYLQrzQwt2CwIGT9KkAUv7eO7ZTMDsG8MYNnRPGXKgjSZqBSW0MCq7ksz1/P
|
||||||
|
dHTJmSECgYEA9Q8VbnLkdk5NlRCNTONfjLhNUGmAqt/qLPi7tnJa0wfSpr3tg1Aj
|
||||||
|
AxnV455fT81vbDP8V8tGDNx+/d5jBkIsdsCYOMa6dqZr1HOfSgbH8wETtU9mbKdB
|
||||||
|
Gt/frdM3rkJvyd5RjoqMex4U7x4f1OWfThHVzvi8TzqfRSXNpbXBMdUCgYEA4tVa
|
||||||
|
TBQhwgnjMHl9OYrW1urtf2fKEGOZA/uGJI55O7IL2SoYnVjFUHjlE4D94K/hdijp
|
||||||
|
l2oFydD0GrWVkWULPvTdUFzMTRPH+SUfXbVqXBMJfHJYrRuZHsJBH2jIwIzu4LaA
|
||||||
|
hliletIdJUtK8mUvwEb+4TlVUwfCYqkveu1EuhkCgYAHjK5pV6rIJkNnmznvK3YP
|
||||||
|
HMJs/sMTAJDzT7pgtYcsxynrLyC5EefyOYKIX6GqELclCzjz73Q6AzT6VzaPw8wg
|
||||||
|
4HAQF7c43oml4uX+XtUcHGViCY8rO7/atxjp/v7RJITTIEE89fG7/UJB15i9c1GE
|
||||||
|
EzKWDL2oZzLu62o5d676/QKBgQDgiCBhvmvMDs18ZkW2d+BBzTpaKvqxTmVgs9EM
|
||||||
|
zpripFNmG21SE1T9Wy4mKEEl7/NVaxoObzxbkSKQbb4ntcV0BB4uNi1k/ner/zsV
|
||||||
|
H0aw7YcuUGHGuNLQx6h+1tIhB2BNv1lposXq1aFUETuWxOKHib8yYfY7wiqATshY
|
||||||
|
/hRRwQKBgCkT2Wt/XjbXLCpX1viEJOLzYYQkrNyPvruXknCnQQ+0/cq4LoRqWZZR
|
||||||
|
4RjS0RkoOF4vM38u/hgICiUUCR4bYUy/4O7uhNu5tDle6lIeB+ZxVpRRedE0OfQT
|
||||||
|
tCpQd0yN9iONBlGyfAtpq7oNzuXgsl1OR4usZ8wCinfImChAl6wv
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
// Generated by unplugin-vue-components
|
||||||
|
// Read more: https://github.com/vuejs/core/pull/3399
|
||||||
|
// biome-ignore lint: disable
|
||||||
|
export {}
|
||||||
|
|
||||||
|
/* prettier-ignore */
|
||||||
|
declare module 'vue' {
|
||||||
|
export interface GlobalComponents {
|
||||||
|
BackButton: typeof import('./src/components/back-button/index.vue')['default']
|
||||||
|
Carte: typeof import('./src/components/restaurant-cart/carte.vue')['default']
|
||||||
|
CheckoutBtn: typeof import('./src/components/restaurant-cart/checkout-btn.vue')['default']
|
||||||
|
ChooseLoation: typeof import('./src/components/checkout/chooseLoation.vue')['default']
|
||||||
|
ChooseLocation: typeof import('./src/components/checkout/chooseLocation.vue')['default']
|
||||||
|
ChoosePeople: typeof import('./src/components/checkout/choosePeople.vue')['default']
|
||||||
|
ChooseTime: typeof import('./src/components/checkout/chooseTime.vue')['default']
|
||||||
|
ElDivider: typeof import('element-plus/es')['ElDivider']
|
||||||
|
HealthCare: typeof import('./src/components/shopping-cart/health-care.vue')['default']
|
||||||
|
NutActionSheet: typeof import('@nutui/nutui')['ActionSheet']
|
||||||
|
NutBadge: typeof import('@nutui/nutui')['Badge']
|
||||||
|
NutButton: typeof import('@nutui/nutui')['Button']
|
||||||
|
NutCalendar: typeof import('@nutui/nutui')['Calendar']
|
||||||
|
NutCard: typeof import('@nutui/nutui')['Card']
|
||||||
|
NutCell: typeof import('@nutui/nutui')['Cell']
|
||||||
|
NutCellGroup: typeof import('@nutui/nutui')['CellGroup']
|
||||||
|
NutCheckbox: typeof import('@nutui/nutui')['Checkbox']
|
||||||
|
NutCheckboxGroup: typeof import('@nutui/nutui')['CheckboxGroup']
|
||||||
|
NutDivider: typeof import('@nutui/nutui')['Divider']
|
||||||
|
NutDrag: typeof import('@nutui/nutui')['Drag']
|
||||||
|
NutElevator: typeof import('@nutui/nutui')['Elevator']
|
||||||
|
NutForm: typeof import('@nutui/nutui')['Form']
|
||||||
|
NutFormItem: typeof import('@nutui/nutui')['FormItem']
|
||||||
|
NutGrid: typeof import('@nutui/nutui')['Grid']
|
||||||
|
NutGridItem: typeof import('@nutui/nutui')['GridItem']
|
||||||
|
NutImage: typeof import('@nutui/nutui')['Image']
|
||||||
|
NutImagePreview: typeof import('@nutui/nutui')['ImagePreview']
|
||||||
|
NutInput: typeof import('@nutui/nutui')['Input']
|
||||||
|
NutInputNumber: typeof import('@nutui/nutui')['InputNumber']
|
||||||
|
NutList: typeof import('@nutui/nutui')['List']
|
||||||
|
NutMenu: typeof import('@nutui/nutui')['Menu']
|
||||||
|
NutMenuItem: typeof import('@nutui/nutui')['MenuItem']
|
||||||
|
NutNavbar: typeof import('@nutui/nutui')['Navbar']
|
||||||
|
NutPopup: typeof import('@nutui/nutui')['Popup']
|
||||||
|
NutPrice: typeof import('@nutui/nutui')['Price']
|
||||||
|
NutRadio: typeof import('@nutui/nutui')['Radio']
|
||||||
|
NutRadioGroup: typeof import('@nutui/nutui')['RadioGroup']
|
||||||
|
NutSearchbar: typeof import('@nutui/nutui')['Searchbar']
|
||||||
|
NutSticky: typeof import('@nutui/nutui')['Sticky']
|
||||||
|
NutSwiper: typeof import('@nutui/nutui')['Swiper']
|
||||||
|
NutSwiperItem: typeof import('@nutui/nutui')['SwiperItem']
|
||||||
|
NutSwitch: typeof import('@nutui/nutui')['Switch']
|
||||||
|
NutTabbar: typeof import('@nutui/nutui')['Tabbar']
|
||||||
|
NutTabbarItem: typeof import('@nutui/nutui')['TabbarItem']
|
||||||
|
NutTabPane: typeof import('@nutui/nutui')['TabPane']
|
||||||
|
NutTabs: typeof import('@nutui/nutui')['Tabs']
|
||||||
|
NutTextarea: typeof import('@nutui/nutui')['Textarea']
|
||||||
|
NutToast: typeof import('@nutui/nutui')['Toast']
|
||||||
|
NutUploader: typeof import('@nutui/nutui')['Uploader']
|
||||||
|
NutVideo: typeof import('@nutui/nutui')['Video']
|
||||||
|
Product: typeof import('./src/components/shopping-cart/product.vue')['default']
|
||||||
|
ResaurantBtn: typeof import('./src/components/restaurant-cart/resaurant-btn.vue')['default']
|
||||||
|
RestaurantCart: typeof import('./src/components/restaurant-cart/index.vue')['default']
|
||||||
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
Share: typeof import('./src/components/shopping-cart/share.vue')['default']
|
||||||
|
ShoppingCart: typeof import('./src/components/shopping-cart/index.vue')['default']
|
||||||
|
SvgComponent: typeof import('./src/components/SvgComponent.vue')['default']
|
||||||
|
SvgIcon: typeof import('./src/components/svg-icon/SvgIcon.vue')['default']
|
||||||
|
Tabbar: typeof import('./src/components/tabbar/index.vue')['default']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
||||||
|
<meta charset="utf-8" name="referrer" content="strict-origin-when-cross-origin" />
|
||||||
|
<!-- WX JS SDK 微信小程序一定要引入这个文件 可以自己去官方下载最新的-->
|
||||||
|
<script type="text/javascript" src="/js/jweixin-1.6.0.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/uni.webview.1.5.6.js" defer></script>
|
||||||
|
<!-- <script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
|
||||||
|
<script>
|
||||||
|
var vConsole = new window.VConsole();
|
||||||
|
</script> -->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
{
|
||||||
|
"name": "vue-app-template",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite --host 0.0.0.0",
|
||||||
|
"build": "vue-tsc -b && vite build",
|
||||||
|
"upload": "bash ./upload.sh",
|
||||||
|
"build-and-upload": "pnpm run build && pnpm run upload",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"check-env": "node ./scripts/checkVersions.cjs"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nutui/nutui": "^4.3.13",
|
||||||
|
"@types/crypto-js": "^4.2.2",
|
||||||
|
"@types/node": "^22.10.1",
|
||||||
|
"@unocss/reset": "^66.3.2",
|
||||||
|
"axios": "^1.7.9",
|
||||||
|
"crypto-js": "^4.2.0",
|
||||||
|
"echarts": "^5.6.0",
|
||||||
|
"lucide-vue-next": "^0.525.0",
|
||||||
|
"pinia": "^2.3.0",
|
||||||
|
"pinia-plugin-persistedstate": "^4.1.3",
|
||||||
|
"semver": "^7.6.3",
|
||||||
|
"vue": "^3.5.13",
|
||||||
|
"vue-echarts": "^7.0.3",
|
||||||
|
"vue-router": "4",
|
||||||
|
"vue-virtual-draglist": "^3.3.8"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@iconify-json/carbon": "^1.2.8",
|
||||||
|
"@nutui/auto-import-resolver": "^1.0.0",
|
||||||
|
"@types/qs": "^6.9.17",
|
||||||
|
"@unocss-applet/preset-rem-rpx": "^0.9.0",
|
||||||
|
"@unocss/preset-wind": "^0.65.2",
|
||||||
|
"@vitejs/plugin-basic-ssl": "^1.2.0",
|
||||||
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
|
"prettier": "3.4.2",
|
||||||
|
"sass-embedded": "^1.86.0",
|
||||||
|
"svg-sprite-loader": "^6.0.11",
|
||||||
|
"typescript": "~5.6.2",
|
||||||
|
"unocss": "^0.65.2",
|
||||||
|
"unplugin-auto-import": "^19.3.0",
|
||||||
|
"unplugin-vue-components": "^28.8.0",
|
||||||
|
"vite": "^6.0.1",
|
||||||
|
"vue-tsc": "^2.1.10"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22.11.0",
|
||||||
|
"pnpm": ">=9.13.2"
|
||||||
|
},
|
||||||
|
"preinstall": "npx only-allow pnpm"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
|
||||||
|
// /plugins/svgBuilder.ts
|
||||||
|
import {readFileSync, readdirSync} from 'fs'
|
||||||
|
import {join as pathJoin} from 'path'
|
||||||
|
import {Plugin} from 'vite'
|
||||||
|
|
||||||
|
let idPrefix = ''
|
||||||
|
const svgTitle = /<svg([^>+].*?)>/
|
||||||
|
const clearHeightWidth = /(width|height)="([^>+].*?)"/g
|
||||||
|
const clearFill = /fill="[^>+].*?"/g;
|
||||||
|
|
||||||
|
const hasViewBox = /(viewBox="[^>+].*?")/g
|
||||||
|
|
||||||
|
const clearReturn = /(\r)|(\n)/g
|
||||||
|
|
||||||
|
const findSvgFile = (dir: string) => {
|
||||||
|
const svgRes: string[] = []
|
||||||
|
const directory = readdirSync(dir, {withFileTypes: true})
|
||||||
|
for (const dirent of directory) {
|
||||||
|
if (dirent?.isDirectory()) {
|
||||||
|
svgRes.push(...findSvgFile(pathJoin(dir, dirent.name, '/')))
|
||||||
|
} else {
|
||||||
|
const svg = readFileSync(pathJoin(dir, dirent.name))
|
||||||
|
.toString()
|
||||||
|
.replace(clearReturn, '')
|
||||||
|
.replace(clearFill,'')
|
||||||
|
.replace(svgTitle, (_$1: string, $2: string) => {
|
||||||
|
let width = '0'
|
||||||
|
let height = '0'
|
||||||
|
let content = $2.replace(
|
||||||
|
clearHeightWidth,
|
||||||
|
(_s1, s2:string, s3:string) => {
|
||||||
|
if (s2 === 'width') {
|
||||||
|
width = s3
|
||||||
|
} else if (s2 === 'height') {
|
||||||
|
height = s3
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (!hasViewBox.test($2)) {
|
||||||
|
content += `viewBox="0 0 ${width} ${height}"`
|
||||||
|
}
|
||||||
|
return `<symbol id="${idPrefix}-${dirent.name.replace(
|
||||||
|
'.svg',
|
||||||
|
''
|
||||||
|
)}" ${content}>`
|
||||||
|
})
|
||||||
|
.replace('</svg>', '</symbol>')
|
||||||
|
svgRes.push(svg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return svgRes
|
||||||
|
}
|
||||||
|
|
||||||
|
const svgBuilder = (path: string, prefix = 'icon'): Plugin => {
|
||||||
|
idPrefix = prefix
|
||||||
|
const res = findSvgFile(path)
|
||||||
|
return {
|
||||||
|
name: 'svg-transform',
|
||||||
|
transformIndexHtml(html) {
|
||||||
|
return html.replace(
|
||||||
|
'<body>',
|
||||||
|
`<body><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">${res.join('')}</svg>`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default svgBuilder
|
||||||
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 927 B |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 302 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 716 B |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 286 KiB |
|
|
@ -0,0 +1,18 @@
|
||||||
|
const semver = require('semver')
|
||||||
|
const { engines } = require('../package')// 这里的package是你的package.json文件路径
|
||||||
|
const version = engines.node
|
||||||
|
|
||||||
|
if (!semver.satisfies(process.version, version)) {
|
||||||
|
console.log(
|
||||||
|
[
|
||||||
|
'node环境变量版本错误',
|
||||||
|
'你的node环境启动位置' + process.execPath + '.',
|
||||||
|
'要求环境版本' + version,
|
||||||
|
'你的环境版本' + process.version
|
||||||
|
].join('\n')
|
||||||
|
)
|
||||||
|
process.exit(1)
|
||||||
|
}else{
|
||||||
|
console.log(`echo check environment successfully`)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col h-screen w-screen pb-safe">
|
||||||
|
<router-view v-slot="{ Component,route }">
|
||||||
|
<keep-alive :include="include" :exclude="excludes">
|
||||||
|
<component :is="Component" :key="route.path" class="flex-auto overflow-auto"/>
|
||||||
|
</keep-alive>
|
||||||
|
</router-view>
|
||||||
|
<router-view name="tabbar">
|
||||||
|
</router-view>
|
||||||
|
<router-view name="backButton"></router-view>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { excludes } from '@/router/keepAlive'
|
||||||
|
|
||||||
|
const include:string[] = []
|
||||||
|
|
||||||
|
// window.addEventListener('message', function(event) {
|
||||||
|
// // event.data 包含了应用发送的消息
|
||||||
|
// const message = event.data;
|
||||||
|
// console.log('收到应用消息:', message);
|
||||||
|
// // 在这里处理接收到的消息
|
||||||
|
// });
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* @Author: HideInMatrix
|
||||||
|
* @Date: 2024-07-15
|
||||||
|
* @LastEditors: error: git config user.name & please set dead value or install git
|
||||||
|
* @LastEditTime: 2024-09-01
|
||||||
|
* @Description: 请求封装
|
||||||
|
* @FilePath: /free-music-react/src/lib/customFetch.ts
|
||||||
|
*/
|
||||||
|
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
|
||||||
|
|
||||||
|
interface FetchOptions extends RequestInit {
|
||||||
|
headers?: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApiResponse {
|
||||||
|
result?: unknown;
|
||||||
|
error?: string;
|
||||||
|
code?: number;
|
||||||
|
success?: boolean;
|
||||||
|
message?:string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiClient = (method: HttpMethod) => {
|
||||||
|
return async (
|
||||||
|
url: string,
|
||||||
|
data?: unknown,
|
||||||
|
options: FetchOptions = {}
|
||||||
|
): Promise<ApiResponse> => {
|
||||||
|
const config: FetchOptions = {
|
||||||
|
method,
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (method !== "GET" && data) {
|
||||||
|
config.body = JSON.stringify(data);
|
||||||
|
} else if (method === "GET" && data) {
|
||||||
|
const _params = [];
|
||||||
|
for (const [key, value] of Object.entries(data)) {
|
||||||
|
_params.push(`${key}=${value}`);
|
||||||
|
}
|
||||||
|
url += `?${_params.join("&")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`${url}`, config);
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
return {
|
||||||
|
error: result.message || "Request failed",
|
||||||
|
code: response.status,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getRequest = apiClient("GET");
|
||||||
|
export const postRequest = apiClient("POST");
|
||||||
|
export const putRequest = apiClient("PUT");
|
||||||
|
export const deleteRequest = apiClient("DELETE");
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// 对指定请求链接进行加密
|
||||||
|
|
||||||
|
import { AxiosRequestConfig } from "axios";
|
||||||
|
import { useUserStore } from "@/store/user";
|
||||||
|
|
||||||
|
export const handleUrl = async (config: (AxiosRequestConfig & {unEncrypt?: boolean})) => {
|
||||||
|
const userStore = useUserStore()
|
||||||
|
if(userStore.getToken){
|
||||||
|
config.headers!!["Authorization"] = `Bearer ${userStore.getToken}`
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
const baseUrl = ''
|
||||||
|
|
||||||
|
export const getMapSearch = () => {
|
||||||
|
return `http://api.tianditu.gov.cn/v2/search`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getLocationByReverseGeography = () => {
|
||||||
|
return `http://api.tianditu.gov.cn/geocoder`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getAdministrative =() => {
|
||||||
|
return `http://api.tianditu.gov.cn/v2/administrative`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getProductCategory = () => {
|
||||||
|
return `${baseUrl}/api/mall/category/list`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getMallRecommend = () => {
|
||||||
|
return `${baseUrl}/api/mall/recommend`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getUserAddressList = () => {
|
||||||
|
return `${baseUrl}/api/user/address/list`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setUserDefaultAddress = () => {
|
||||||
|
return `${baseUrl}/api/user/address/set-default`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getProductInfo = () => {
|
||||||
|
return `${baseUrl}/api/product/detail`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getNotifyList = () => {
|
||||||
|
return `${baseUrl}/api/notice/list`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getRestaurantFoodList = () => {
|
||||||
|
return `${baseUrl}/api/product/simple-list`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getRestaurantFoodDetail = () => {
|
||||||
|
return `${baseUrl}/api/meal/product/detail`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getRestaurantFoodSet = () => {
|
||||||
|
return `${baseUrl}/api/product/setmeal-list`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getLessonCategory = () => {
|
||||||
|
return `${baseUrl}/api/lesson/category`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getLessonList = () => {
|
||||||
|
return `${baseUrl}/api/lesson/list`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getCourseDetail = () => {
|
||||||
|
return `${baseUrl}/api/course/detail`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getLessonCatalog = () => {
|
||||||
|
return `${baseUrl}/api/lesson/catalog`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getNursingHomeList = () => {
|
||||||
|
return `${baseUrl}/api/nursing-home/list`
|
||||||
|
}
|
||||||
|
export const getNursingDetail = () => {
|
||||||
|
return `${baseUrl}/api/nursing/detail`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getTravelList = () => {
|
||||||
|
return `${baseUrl}/api/travel/list`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getHospitalCompanionList = () =>{
|
||||||
|
return `${baseUrl}/api/nursing/hospital-companion/list`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getServiceDetail = () =>{
|
||||||
|
return `${baseUrl}/api/service/detail`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getHousekeepingCleaningList = () =>{
|
||||||
|
return `${baseUrl}/api/housekeeping/cleaning/list`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getHousekeepingCleaningDetail = () =>{
|
||||||
|
return `${baseUrl}/api/housekeeping/cleaning/detail`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
// @ts-nocheck
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
// Generated by unplugin-auto-import
|
||||||
|
// biome-ignore lint: disable
|
||||||
|
export {}
|
||||||
|
declare global {
|
||||||
|
const EffectScope: typeof import('vue')['EffectScope']
|
||||||
|
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
|
||||||
|
const computed: typeof import('vue')['computed']
|
||||||
|
const createApp: typeof import('vue')['createApp']
|
||||||
|
const createPinia: typeof import('pinia')['createPinia']
|
||||||
|
const customRef: typeof import('vue')['customRef']
|
||||||
|
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
|
||||||
|
const defineComponent: typeof import('vue')['defineComponent']
|
||||||
|
const defineStore: typeof import('pinia')['defineStore']
|
||||||
|
const effectScope: typeof import('vue')['effectScope']
|
||||||
|
const getActivePinia: typeof import('pinia')['getActivePinia']
|
||||||
|
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||||
|
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||||
|
const h: typeof import('vue')['h']
|
||||||
|
const inject: typeof import('vue')['inject']
|
||||||
|
const isProxy: typeof import('vue')['isProxy']
|
||||||
|
const isReactive: typeof import('vue')['isReactive']
|
||||||
|
const isReadonly: typeof import('vue')['isReadonly']
|
||||||
|
const isRef: typeof import('vue')['isRef']
|
||||||
|
const mapActions: typeof import('pinia')['mapActions']
|
||||||
|
const mapGetters: typeof import('pinia')['mapGetters']
|
||||||
|
const mapState: typeof import('pinia')['mapState']
|
||||||
|
const mapStores: typeof import('pinia')['mapStores']
|
||||||
|
const mapWritableState: typeof import('pinia')['mapWritableState']
|
||||||
|
const markRaw: typeof import('vue')['markRaw']
|
||||||
|
const nextTick: typeof import('vue')['nextTick']
|
||||||
|
const onActivated: typeof import('vue')['onActivated']
|
||||||
|
const onBeforeMount: typeof import('vue')['onBeforeMount']
|
||||||
|
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
|
||||||
|
const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
|
||||||
|
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
|
||||||
|
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
|
||||||
|
const onDeactivated: typeof import('vue')['onDeactivated']
|
||||||
|
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
|
||||||
|
const onMounted: typeof import('vue')['onMounted']
|
||||||
|
const onRenderTracked: typeof import('vue')['onRenderTracked']
|
||||||
|
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
|
||||||
|
const onScopeDispose: typeof import('vue')['onScopeDispose']
|
||||||
|
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
|
||||||
|
const onUnmounted: typeof import('vue')['onUnmounted']
|
||||||
|
const onUpdated: typeof import('vue')['onUpdated']
|
||||||
|
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
|
||||||
|
const provide: typeof import('vue')['provide']
|
||||||
|
const reactive: typeof import('vue')['reactive']
|
||||||
|
const readonly: typeof import('vue')['readonly']
|
||||||
|
const ref: typeof import('vue')['ref']
|
||||||
|
const resolveComponent: typeof import('vue')['resolveComponent']
|
||||||
|
const setActivePinia: typeof import('pinia')['setActivePinia']
|
||||||
|
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
|
||||||
|
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||||
|
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||||
|
const shallowRef: typeof import('vue')['shallowRef']
|
||||||
|
const storeToRefs: typeof import('pinia')['storeToRefs']
|
||||||
|
const toRaw: typeof import('vue')['toRaw']
|
||||||
|
const toRef: typeof import('vue')['toRef']
|
||||||
|
const toRefs: typeof import('vue')['toRefs']
|
||||||
|
const toValue: typeof import('vue')['toValue']
|
||||||
|
const triggerRef: typeof import('vue')['triggerRef']
|
||||||
|
const unref: typeof import('vue')['unref']
|
||||||
|
const useAttrs: typeof import('vue')['useAttrs']
|
||||||
|
const useCssModule: typeof import('vue')['useCssModule']
|
||||||
|
const useCssVars: typeof import('vue')['useCssVars']
|
||||||
|
const useId: typeof import('vue')['useId']
|
||||||
|
const useLink: typeof import('vue-router')['useLink']
|
||||||
|
const useModel: typeof import('vue')['useModel']
|
||||||
|
const useRoute: typeof import('vue-router')['useRoute']
|
||||||
|
const useRouter: typeof import('vue-router')['useRouter']
|
||||||
|
const useSlots: typeof import('vue')['useSlots']
|
||||||
|
const useTemplateRef: typeof import('vue')['useTemplateRef']
|
||||||
|
const watch: typeof import('vue')['watch']
|
||||||
|
const watchEffect: typeof import('vue')['watchEffect']
|
||||||
|
const watchPostEffect: typeof import('vue')['watchPostEffect']
|
||||||
|
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
|
||||||
|
}
|
||||||
|
// for type re-export
|
||||||
|
declare global {
|
||||||
|
// @ts-ignore
|
||||||
|
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
||||||
|
import('vue')
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<template>
|
||||||
|
<div class="block">
|
||||||
|
<i v-html="content"></i>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
|
||||||
|
content:{
|
||||||
|
type:String,
|
||||||
|
default: ""
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
<template>
|
||||||
|
<nut-drag direction="y" attract :style="[props.bottom ? {bottom:props.bottom} : {top:props.top}]" class="rounded-full bg-[#fff] border-solid border-black border-[3rpx] left-[30rpx]" style="z-index: 999 !important;">
|
||||||
|
<ChevronLeft class="w-[88rpx] h-[88rpx] text-black" :stroke-width="2" @click="goBack"/>
|
||||||
|
</nut-drag>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ChevronLeft } from 'lucide-vue-next';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
bottom:{
|
||||||
|
type:String,
|
||||||
|
default:""
|
||||||
|
},
|
||||||
|
top:{
|
||||||
|
type:String,
|
||||||
|
default:"8vh"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const goBack = () => {
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
<template>
|
||||||
|
<nut-action-sheet v-model:visible="innerShow">
|
||||||
|
<div class="flex flex-col bg-[#F5F5F5]">
|
||||||
|
<div class="relative py-[32rpx] text-center bg-white">
|
||||||
|
<div class="text-[36rpx] font-500">选择收货地址</div>
|
||||||
|
<div class="w-[60rpx] h-[60rpx] bg-[#f5f5f5] rounded-full absolute top-[20rpx] right-[20rpx] flex items-center justify-center" @click="handleClose">
|
||||||
|
<X class="text-[25rpx]" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="px-[30rpx] py-[20rpx] min-h-0 flex-auto overflow-auto">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in recvAddrList"
|
||||||
|
:key="index"
|
||||||
|
class="items-center justify-between h-[210rpx] rounded-[24rpx] bg-white pt-[30rpx] pl-[30rpx] pb-[22rpx] pr-[34rpx] flex mt-[30rpx]"
|
||||||
|
:class="{ 'active-address': item.isDefault }"
|
||||||
|
@click="handleSelectAddress(item)">
|
||||||
|
<div class="flex flex-col justify-evenly flex-1 h-full">
|
||||||
|
<div class="text-[36rpx] font-600">{{ item.fullAddress }}</div>
|
||||||
|
<div class="text-[32rpx] text-[#333] font-400 flex items-center mt-[14rpx]">
|
||||||
|
<div class="mr-[20rpx]">{{ item.name }}</div>
|
||||||
|
<div>{{ item.phone }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="h-[125rpx] py-[12rpx] mr-[30rpx]">
|
||||||
|
<div class="border-r-[#CCCCCC] border-dashed border-r-[2rpx] h-full w-[1rpx]"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col items-center" @click="handleEditAddress(item)">
|
||||||
|
<PencilLine class="w-[48rpx] h-[48rpx] text-[#29bebb]" />
|
||||||
|
<div class="text-[#28BEBB] text-[32rpx] font-400 mt-[8rpx]">修改</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Check class="text-[#fff] absolute bottom-[-10rpx] right-[-5rpx] font-700" v-if="item.isDefault" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="px-[30rpx] py-[16rpx] bg-white">
|
||||||
|
<div class="pb-safe">
|
||||||
|
<div class="flex items-center justify-center bg-[#28BEBB] rounded-[20rpx] px-[80rpx] py-[28rpx]" @click="toAddAddressPage">
|
||||||
|
<CirclePlus class="text-white mr-[16rpx]" fill="#fff" stroke="#29bebb" :stroke-width="2"/>
|
||||||
|
<span class="text-white">新增收货地址</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nut-action-sheet>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useUserStore } from "@/store/user";
|
||||||
|
import { X, PencilLine, CirclePlus, Check } from "lucide-vue-next";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
show: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const innerShow = computed({
|
||||||
|
get() {
|
||||||
|
return props.show;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
emit("update:show", value);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const recvAddrList = computed(() => userStore.recvAddrList);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const emit = defineEmits(["update:show"]);
|
||||||
|
|
||||||
|
const handleEditAddress = (item: any) => {
|
||||||
|
router.push({ path: "/mine/address", query: { id: item.id } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const toAddAddressPage = () => {
|
||||||
|
router.push({ path: "/mine/add/address" });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSelectAddress = (item: any) => {
|
||||||
|
userStore.setDefaultAddress(item);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
emit("update:show", false);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.active-address {
|
||||||
|
outline: 2px solid #000000;
|
||||||
|
position: relative;
|
||||||
|
& ::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border: 15px solid;
|
||||||
|
border-color: transparent #000 #000 transparent;
|
||||||
|
|
||||||
|
border-bottom-right-radius: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
<template>
|
||||||
|
<nut-action-sheet v-model:visible="innerShow">
|
||||||
|
<div class="flex flex-col bg-[#F5F5F5]">
|
||||||
|
<div class="relative py-[32rpx] text-center bg-white">
|
||||||
|
<div class="text-[36rpx] font-500">选择被护理人</div>
|
||||||
|
<div class="w-[60rpx] h-[60rpx] bg-[#f5f5f5] rounded-full absolute top-[20rpx] right-[20rpx] flex items-center justify-center" @click="handleClose">
|
||||||
|
<X class="text-[25rpx]" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="px-[30rpx] py-[20rpx] min-h-0 flex-auto overflow-auto">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in recvAddrList"
|
||||||
|
:key="index"
|
||||||
|
class="items-center justify-between h-[210rpx] rounded-[24rpx] bg-white p-[30rpx] flex mt-[30rpx]"
|
||||||
|
:class="{ 'active-address': item.isDefault }"
|
||||||
|
@click="handleSelectAddress(item)">
|
||||||
|
<div class="flex flex-col justify-evenly flex-1 h-full text-[32rpx] font-400 text-[#333]">
|
||||||
|
<div class="flex items-center mt-[14rpx]">
|
||||||
|
<div class="w-[72rpx] h-[38rpx] rounded-[10rpx] bg-[#e8f2ff] text-[26rpx] text-[#1D86FF] flex items-center justify-center mr-[10rpx]" v-if="item.isDefault">默认</div>
|
||||||
|
<div class="mr-[20rpx]">{{ item.name }}</div>
|
||||||
|
</div>
|
||||||
|
<div>{{ item.phone }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="h-[125rpx] py-[12rpx] mr-[30rpx]">
|
||||||
|
<div class="border-r-[#CCCCCC] border-dashed border-r-[2rpx] h-full w-[1rpx]"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col items-center" @click="handleEditAddress(item)">
|
||||||
|
<PencilLine class="w-[48rpx] h-[48rpx] text-[#29bebb]" />
|
||||||
|
<div class="text-[#28BEBB] text-[32rpx] font-400 mt-[8rpx]">修改</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Check class="text-[#fff] absolute bottom-[-10rpx] right-[-5rpx] font-700" v-if="item.isDefault" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="px-[30rpx] py-[16rpx] bg-white">
|
||||||
|
<div class="pb-safe">
|
||||||
|
<div class="flex items-center justify-center bg-[#28BEBB] rounded-[20rpx] px-[80rpx] py-[28rpx]" @click="toAddAddressPage">
|
||||||
|
<CirclePlus class="text-white mr-[16rpx] size-[54rpx]" fill="#fff" stroke="#29bebb" :stroke-width="2"/>
|
||||||
|
<span class="text-white text-[44rpx]">新增被护理人</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nut-action-sheet>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useUserStore } from "@/store/user";
|
||||||
|
import { X, PencilLine, CirclePlus, Check } from "lucide-vue-next";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
show: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const innerShow = computed({
|
||||||
|
get() {
|
||||||
|
return props.show;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
emit("update:show", value);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const recvAddrList = ref([
|
||||||
|
{ name: "王大力(女,67岁)", phone: "310108196005127765", isDefault: true },
|
||||||
|
{ name: "王小力(女,67岁)", phone: "310108196005127765", isDefault: false },
|
||||||
|
]);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const emit = defineEmits(["update:show"]);
|
||||||
|
|
||||||
|
const handleEditAddress = (item: any) => {
|
||||||
|
router.push({ path: "/mine/add/paramedic", query: { id: item.id } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const toAddAddressPage = () => {
|
||||||
|
router.push({ path: "/mine/add/paramedic" });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSelectAddress = (item: any) => {
|
||||||
|
|
||||||
|
recvAddrList.value = recvAddrList.value.map(item => {
|
||||||
|
if (item.isDefault) {
|
||||||
|
item.isDefault = false;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
item.isDefault = true;
|
||||||
|
// userStore.setDefaultAddress(item);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
emit("update:show", false);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.active-address {
|
||||||
|
outline: 2px solid #000000;
|
||||||
|
position: relative;
|
||||||
|
& ::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border: 15px solid;
|
||||||
|
border-color: transparent #000 #000 transparent;
|
||||||
|
|
||||||
|
border-bottom-right-radius: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
<template>
|
||||||
|
<nut-action-sheet v-model:visible="innerShow">
|
||||||
|
<div class="flex flex-col bg-[#F5F5F5]">
|
||||||
|
<div class="relative py-[32rpx] text-center bg-white">
|
||||||
|
<div class="text-[36rpx] font-500">选择服务时长</div>
|
||||||
|
<div class="w-[60rpx] h-[60rpx] bg-[#f5f5f5] rounded-full absolute top-[20rpx] right-[20rpx] flex items-center justify-center" @click="handleClose">
|
||||||
|
<X class="text-[25rpx]" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="px-[30rpx] py-[20rpx] min-h-0 flex-auto overflow-auto">
|
||||||
|
<nut-radio-group v-model="chooseTime" class="flex time-radio-group" direction="horizontal">
|
||||||
|
<nut-radio v-for="item in timeList" :key="item.value" :label="item.value" shape="button">{{ item.label }}</nut-radio>
|
||||||
|
</nut-radio-group>
|
||||||
|
|
||||||
|
<div class="text-[32rpx] text-[#999AA3] mt-[20rpx]">2小时适合面积≤50平米房屋</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="px-[30rpx] py-[16rpx] bg-white">
|
||||||
|
<div class="pb-safe">
|
||||||
|
<div class="flex items-center justify-center bg-[#28BEBB] rounded-[20rpx] px-[80rpx] py-[28rpx]" @click="submit">
|
||||||
|
<span class="text-white">确定</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nut-action-sheet>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { X } from "lucide-vue-next";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
show: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const innerShow = computed({
|
||||||
|
get() {
|
||||||
|
return props.show;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
emit("update:show", value);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const chooseTime = ref(2);
|
||||||
|
|
||||||
|
const timeList = [
|
||||||
|
{
|
||||||
|
label: "2小时",
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "3小时",
|
||||||
|
value: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "4小时",
|
||||||
|
value: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "5小时",
|
||||||
|
value: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "6小时",
|
||||||
|
value: 6,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
const emit = defineEmits(["update:show",'onGetTime']);
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
handleClose();
|
||||||
|
emit("onGetTime",chooseTime.value)
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
emit("update:show", false);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.active-address {
|
||||||
|
outline: 2px solid #000000;
|
||||||
|
position: relative;
|
||||||
|
& ::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border: 15px solid;
|
||||||
|
border-color: transparent #000 #000 transparent;
|
||||||
|
|
||||||
|
border-bottom-right-radius: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-radio-group {
|
||||||
|
--nut-radio-button-padding: 13px 32px;
|
||||||
|
--nut-radio-button-border-radius: 5px;
|
||||||
|
--nut-radio-label-font-active-color:#0AB3B0;
|
||||||
|
--nut-radio-label-button-border-color: #28BEBB;
|
||||||
|
--nut-radio-label-button-background: #e9f8f8;
|
||||||
|
--nut-radio-button-font-size:18px;
|
||||||
|
|
||||||
|
:deep(.nut-radio__button){
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
<template>
|
||||||
|
<nut-list v-if="carte.length > 0" :list-data="carte" @scroll-bottom="onScrollBottom" class="mt-[30rpx] flex-1 min-h-0 overflow-auto">
|
||||||
|
<template #default="{ item }">
|
||||||
|
<div class="bg-white mx-[30rpx] rounded-[20rpx] flex mb-[30rpx]">
|
||||||
|
<img :src="item.cover" alt="food" class="h-[176rpx] aspect-square rounded-[20rpx_0_0_20rpx] object-cover" />
|
||||||
|
<div class="px-[20rpx] flex-auto flex flex-col justify-between">
|
||||||
|
<p class="text-[40rpx] font-500">{{ item.name }}</p>
|
||||||
|
<p class="text-[30rpx] font-400 text-[#666] mb-[26rpx] mt-[10rpx]">{{ item.desc }}</p>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex items-baseline">
|
||||||
|
<nut-price :price="item.price" :decimal-places="2" size="normal" class="font-600" />
|
||||||
|
<span class="text-[28rpx] font-400 text-[#adafaf]">/份</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nut-input-number v-model="item.count" :readonly="true" @reduce="(event) => handleMinus(event, item)" @add="handlePlus(item)" :min="0">
|
||||||
|
<template #left-icon>
|
||||||
|
<div class="w-[52rpx] h-[52rpx] bg-white rounded-full flex items-center justify-center border-[#28BEBB] border-[4rpx] border-solid">
|
||||||
|
<Minus :stroke-width="3" class="text-[#28BEBB]" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #right-icon>
|
||||||
|
<div class="w-[52rpx] h-[52rpx] bg-[#28BEBB] rounded-full flex items-center justify-center">
|
||||||
|
<Plus :stroke-width="3" class="text-white" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</nut-input-number>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</nut-list>
|
||||||
|
<div v-else class="flex-1 min-h-0 overflow-auto flex items-center justify-center">
|
||||||
|
<p class="text-[32rpx] font-500">暂无商品</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { Plus, Minus } from "lucide-vue-next";
|
||||||
|
import { useShoppingCartStore } from "@/store/shoppingCart";
|
||||||
|
|
||||||
|
const shoppingCartStore = useShoppingCartStore();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
carte: {
|
||||||
|
type: Array as PropType<any[]>,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onScrollBottom = () => {
|
||||||
|
console.log("onScrollBottom");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMinus = (event: any, item: any) => {
|
||||||
|
const orderCount = Number(item.count) - 1;
|
||||||
|
if(orderCount <= 0){
|
||||||
|
shoppingCartStore.removeShoppingCart(item.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePlus = (item: any) => {
|
||||||
|
// console.log("handlePlus", item);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.nut-input-number {
|
||||||
|
--nut-inputnumber-input-background-color: #fff;
|
||||||
|
--nut-inputnumber-input-margin: 0;
|
||||||
|
--nut-inputnumber-input-font-size: 18px;
|
||||||
|
--nut-inputnumber-input-width: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
<template>
|
||||||
|
<div class="h-[120rpx] rounded-full bg-[#25262A] flex">
|
||||||
|
<nut-badge :value="shoppingCartTotal" top="10" right="5" @click="showShoppingCart">
|
||||||
|
<img src="/images/checkout/basket.png" alt="" class="w-[100rpx] h-[100rpx] ml-[30rpx] my-[10rpx]" />
|
||||||
|
</nut-badge>
|
||||||
|
<div class="flex-1 min-w-0 flex flex-col justify-center ml-[6rpx]">
|
||||||
|
<nut-price :price="shoppingCartTotalPrice" size="normal" class="font-700 custom-price mb-[6rpx]" />
|
||||||
|
<div class="flex items-center text-white text-[26rpx]">
|
||||||
|
<div class="mr-[10rpx]">含运费 ¥{{ shippingFee }}</div>
|
||||||
|
<div>打包费 ¥{{ packingFee }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="checkout-button w-[224rpx] relative text-white flex items-center justify-center" @click="handleCheckoutBtn">
|
||||||
|
<div class="text-white z-2 text-[40rpx] font-600">去结算</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { packingFee, shippingFee } from "@/composables/useRestaurant";
|
||||||
|
import { useShoppingCartStore } from "@/store/shoppingCart";
|
||||||
|
|
||||||
|
const shoppingCartStore = useShoppingCartStore();
|
||||||
|
const shoppingCartTotal = computed(() => {
|
||||||
|
return shoppingCartStore.getShoppingCartByRestaurant.reduce((total, item) => total + Number(item.count), 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
const shoppingCartTotalPrice = computed(() => {
|
||||||
|
return shoppingCartStore.getShoppingCartByRestaurant.reduce((total, item) => total + item.price * item.count, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["showShoppingCart","handleCheckoutBtn"]);
|
||||||
|
const showShoppingCart = () => {
|
||||||
|
emit("showShoppingCart");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCheckoutBtn = () => {
|
||||||
|
emit("handleCheckoutBtn");
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.custom-price {
|
||||||
|
--nut-price-symbol-medium-size: 18px;
|
||||||
|
--nut-price-decimal-medium-size: 18px;
|
||||||
|
--nut-price-medium-size: 24px;
|
||||||
|
--nut-primary-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkout-button {
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 50px;
|
||||||
|
bottom: 0;
|
||||||
|
border-radius: 20px 100% 100% 20px;
|
||||||
|
background: #f68e1d;
|
||||||
|
transform: skewX(-15deg);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
border-radius: 0 113px 113px 0;
|
||||||
|
width: 80%;
|
||||||
|
height: 100%;
|
||||||
|
background: #f68e1d;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<CheckoutBtn @showShoppingCart="handleShowShoppingCart" @handleCheckoutBtn="handleShowCheckoutCart" />
|
||||||
|
<nut-action-sheet v-model:visible="show">
|
||||||
|
<div class="flex flex-col pb-safe max-h-[60vh] min-h-[60vh]">
|
||||||
|
<div class="bg-[#F5F5F5] px-[30rpx] py-[18rpx] flex items-center">
|
||||||
|
<span class="text-[#666666] text-[30rpx] font-400">已选菜品</span>
|
||||||
|
<div class="text-[#333] font-400 text-[30rpx]">
|
||||||
|
(含运费
|
||||||
|
<span class="text-[#F03B25] text-[30rpx] font-400">¥{{ shippingFee }}</span>
|
||||||
|
打包费 ¥<span class="text-[#F03B25] text-[30rpx] font-400">{{ packingFee }}</span>)
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center ml-auto">
|
||||||
|
<Trash2 :stroke-width="1.5" class="w-[32rpx] h-[32rpx] text-[#ccc]"/>
|
||||||
|
<span class="text-[#666] font-400 text-[32rpx]" @click="handleClearCart">清空</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Carte :carte="shoppingCartList" />
|
||||||
|
<div class="bg-white px-[30rpx] pb-[16rpx]">
|
||||||
|
<CheckoutBtn />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nut-action-sheet>
|
||||||
|
<nut-action-sheet v-model:visible="showCheckoutCart">
|
||||||
|
<div class="flex flex-col pb-safe max-h-[80vh] min-h-[60vh]">
|
||||||
|
<div class="flex items-center w-full px-[30rpx] py-[30rpx] bg-[#F5F5F5]" @click="handleShowChooseLocation">
|
||||||
|
<img src="/images/checkout/location.png" alt="location" class="w-[92rpx] h-[92rpx]">
|
||||||
|
|
||||||
|
<div class="flex flex-col ml-[20rpx] flex-auto">
|
||||||
|
<p class="text-[36rpx] font-600">{{ defaultAddr?.fullAddress }}</p>
|
||||||
|
<div class="flex items-center text-[32rpx] text-[#333] mt-[14rpx]">
|
||||||
|
<span class="mr-[20rpx]">{{ defaultAddr?.name }}</span>
|
||||||
|
<span>{{ defaultAddr?.phone }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ChevronRight :stroke-width="1.5" class="w-[60rpx] h-[60rpx] ml-auto"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Carte :carte="shoppingCartList" class="px-[30rpx]"/>
|
||||||
|
<div class="mx-[30rpx] border-b-dashed border-b-[#D8D8D8] border-b-[2rpx] mb-[40rpx] mt-[20rpx]"></div>
|
||||||
|
<div class="flex flex-col items-center px-[30rpx]">
|
||||||
|
<div class="flex items-center justify-between w-full">
|
||||||
|
<div class="text-[#3d3d3d] text-[30rpx] font-400">打包费</div>
|
||||||
|
<div class="text-[#F03B25] text-[30rpx] font-400">¥{{ packingFee }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-between w-full mt-[20rpx] mb-[40rpx]">
|
||||||
|
<div class="text-[#3d3d3d] text-[30rpx] font-400">运费</div>
|
||||||
|
<div class="text-[#F03B25] text-[30rpx] font-400">¥{{ shippingFee }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white px-[30rpx] pb-[16rpx]">
|
||||||
|
<CheckoutBtn @handleCheckoutBtn="toCheckout"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nut-action-sheet>
|
||||||
|
<ChooseLocation v-model:show="showChooseLocation" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { packingFee, shippingFee } from "@/composables/useRestaurant";
|
||||||
|
import { Trash2, ChevronRight } from "lucide-vue-next";
|
||||||
|
import CheckoutBtn from "./checkout-btn.vue";
|
||||||
|
import { useShoppingCartStore } from "@/store/shoppingCart";
|
||||||
|
import Carte from "./carte.vue";
|
||||||
|
import { useUserStore } from "@/store/user";
|
||||||
|
import ChooseLocation from "@/components/checkout/chooseLocation.vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
import { CartTypeEnum } from "@/types/cart";
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const { recvAddrList } = storeToRefs(userStore);
|
||||||
|
|
||||||
|
const defaultAddr = computed(() => {
|
||||||
|
return recvAddrList.value.find((item:any) => item.isDefault);
|
||||||
|
});
|
||||||
|
|
||||||
|
const shoppingCartStore = useShoppingCartStore();
|
||||||
|
const shoppingCartList = computed(() => {
|
||||||
|
return shoppingCartStore.getShoppingCartByRestaurant;
|
||||||
|
});
|
||||||
|
|
||||||
|
const show = ref(false);
|
||||||
|
const showCheckoutCart = ref(false);
|
||||||
|
const handleShowShoppingCart = () => {
|
||||||
|
show.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleShowCheckoutCart = () => {
|
||||||
|
showCheckoutCart.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const showChooseLocation = ref(false);
|
||||||
|
const handleShowChooseLocation = () => {
|
||||||
|
showChooseLocation.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toCheckout = () => {
|
||||||
|
router.push({ path: "/checkout/payment", query: { type: CartTypeEnum.Restaurant } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClearCart = () => {
|
||||||
|
shoppingCartStore.clearShoppingCartByType(CartTypeEnum.Restaurant);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
<template>
|
||||||
|
<ul class="px-[30rpx] bg-[#f5f5f5] flex-auto w-full min-h-0 overflow-auto pb-[20rpx]">
|
||||||
|
<li class="p-[20rpx] rounded-[20rpx] bg-white w-full mt-[30rpx]" v-for="(item, index) in shoppingCartList" :key="index">
|
||||||
|
<div class="flex">
|
||||||
|
<img :src="item.cover" alt="cover" class="w-[192rpx] h-[192rpx] rounded-[20rpx]" />
|
||||||
|
<div class="flex flex-col ml-[16rpx] justify-between">
|
||||||
|
<div class="text-[40rpx] font-500">{{ item.name }}</div>
|
||||||
|
<div class="text-[30rpx] text-[#666] font-400 mt-[10rpx]">{{ item.desc }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between items-center mt-[30rpx]">
|
||||||
|
<div class="text-[32rpx] text-[#333] font-400">产品单价</div>
|
||||||
|
<nut-price :price="item.price" size="normal" class="text-[36rpx] font-500" />
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between items-center mt-[22rpx]">
|
||||||
|
<div class="text-[32rpx] text-[#333] font-400">购买数量</div>
|
||||||
|
<nut-input-number v-model="item.count" readonly :min="0" @overlimit="(event, type) => handleOverLimit(event, type, item)"/>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="flex pl-[24rpx] py-[16rpx] pr-[30rpx] items-center justify-between">
|
||||||
|
<nut-price :price="totalPrice" size="large" class="font-500" />
|
||||||
|
<div class="w-[416rpx] h-[100rpx] rounded-[20rpx] bg-[#28BEBB] text-white text-[44rpx] font-600 flex items-center justify-center" @click="toPayment">立即支付</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useShoppingCartStore } from "@/store/shoppingCart";
|
||||||
|
import { CartTypeEnum } from "@/types/cart";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const shoppingCartStore = useShoppingCartStore();
|
||||||
|
const shoppingCartList = ref(shoppingCartStore.getShoppingCartByMall);
|
||||||
|
|
||||||
|
const totalPrice = computed(() => {
|
||||||
|
return shoppingCartList.value.reduce((acc, item) => acc + item.price * item.count, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
const toPayment = () => {
|
||||||
|
router.push({ path: "/checkout/payment", query: { type: CartTypeEnum.Mall } });
|
||||||
|
};
|
||||||
|
const handleOverLimit = (_event:any,type:string,item:any) => {
|
||||||
|
if(type === "reduce"){
|
||||||
|
shoppingCartStore.removeShoppingCart(item.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
:deep(.nut-input-number) {
|
||||||
|
--nut-inputnumber-height: 32px;
|
||||||
|
--nut-inputnumber-input-margin: 0 2px;
|
||||||
|
--nut-inputnumber-input-border-radius: 0;
|
||||||
|
|
||||||
|
& .nut-input-number__icon {
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
& :nth-child(1) {
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& :nth-child(3) {
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
<template>
|
||||||
|
<nut-action-sheet v-model:visible="innerShow" class="share-action">
|
||||||
|
<div class="flex flex-col relative pt-[20rpx] px-[20rpx] pb-[42rpx] bg-gradient-to-b from-[#28BEBB] to-[#28BEBB00] rounded-[40rpx_40rpx_0_0]">
|
||||||
|
<img src="/images/product/share-title.png" alt="share-title" class="w-[276rpx] h-[90rpx] absolute top-[-10%] left-[36rpx]" />
|
||||||
|
<div class="rounded-full bg-white w-[60rpx] h-[60rpx] flex items-center justify-center self-end" @click="handleClose">
|
||||||
|
<X />
|
||||||
|
</div>
|
||||||
|
<div class="m-[34rpx] flex items-center">
|
||||||
|
<img src="/images/product/share-wx.png" alt="share-qrcode" class="w-[320rpx] h-[284rpx]" />
|
||||||
|
<img src="/images/product/share-friend.png" alt="share-friend" class="w-[320rpx] h-[284rpx]" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nut-action-sheet>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { X } from "lucide-vue-next";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
show: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emits = defineEmits(["update:show"]);
|
||||||
|
|
||||||
|
const innerShow = computed({
|
||||||
|
get() {
|
||||||
|
return props.show;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
emits("update:show", value);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
emits("update:show", false);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
export interface SvgIconProps {
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<SvgIconProps>()
|
||||||
|
|
||||||
|
const styles = useCssModule()
|
||||||
|
const attrs = useAttrs()
|
||||||
|
|
||||||
|
const iconName = computed(() => `#icon-${props.name}`);
|
||||||
|
const svgClass = computed(() => {
|
||||||
|
const className = [styles['svg-icon']]
|
||||||
|
if (props.name) className.push(`icon-${props.name}`)
|
||||||
|
return className
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<svg :class="svgClass" v-bind="attrs">
|
||||||
|
<use :xlink:href="iconName"></use>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style module>
|
||||||
|
.svg-icon {
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
fill: currentColor;
|
||||||
|
vertical-align: middle;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import SvgIcon from './SvgIcon.vue'
|
||||||
|
import type {SvgIconProps} from './SvgIcon.vue'
|
||||||
|
|
||||||
|
export {
|
||||||
|
SvgIcon
|
||||||
|
}
|
||||||
|
|
||||||
|
export type {
|
||||||
|
SvgIconProps
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
<template>
|
||||||
|
<nut-tabbar v-model="activeIndex" @tab-switch="tabSwitch" class="">
|
||||||
|
<nut-tabbar-item v-for="(item,index) in menus" :key="index" :to="`${item.href}`">
|
||||||
|
<template #icon="props">
|
||||||
|
<div class="text-[36rpx] font-500 pt-[36rpx] mt-[8rpx]" :class="props.active ? 'active-bg w-[104rpx] h-[86rpx] text-[40rpx]':''">{{ item.name }}</div>
|
||||||
|
</template>
|
||||||
|
</nut-tabbar-item>
|
||||||
|
</nut-tabbar>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const activeIndex = ref(0);
|
||||||
|
|
||||||
|
const menus = [{name:'首页',href:'/home'},{name:'健康',href:"/medical"},{name:'商城',href:'/shop'},{name:'我的',href:'/mine'}]
|
||||||
|
// const routeQuery = Object.entries(route.query).map(([key, value]) => `${key}=${value}`).join("&");
|
||||||
|
|
||||||
|
const tabSwitch = (item: Record<string, unknown>, index: number) => {
|
||||||
|
console.log(item, index);
|
||||||
|
};
|
||||||
|
|
||||||
|
const useMatchPath = () => {
|
||||||
|
const path = route.path;
|
||||||
|
activeIndex.value = menus.findIndex(item => item.href === path)
|
||||||
|
}
|
||||||
|
useMatchPath();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.active-bg{
|
||||||
|
background-image: url("/images/tabbar/menu-active.png");
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
:deep(.nut-badge){
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:root{
|
||||||
|
--nut-tabbar-border-bottom:0px solid;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { getRequest } from "@/api/customFetch";
|
||||||
|
import { getLocationByReverseGeography } from "@/api/interfaceDocument";
|
||||||
|
import { mapKey } from "@/utils/useMap";
|
||||||
|
import { useUserStore } from "@/store/user";
|
||||||
|
import { Address } from "@/types/tdt";
|
||||||
|
|
||||||
|
export const useLocationName = (longitude: string, latitude: string) => {
|
||||||
|
const userStore = useUserStore();
|
||||||
|
userStore.setCoordinate(longitude, latitude);
|
||||||
|
userStore.setDeviceCoordinate(longitude, latitude);
|
||||||
|
getRequest(getLocationByReverseGeography(), {
|
||||||
|
postStr: encodeURIComponent(JSON.stringify({ lon: longitude, lat: latitude, ver: 1 })),
|
||||||
|
type: "geocode",
|
||||||
|
tk: mapKey,
|
||||||
|
}).then((resp) => {
|
||||||
|
let _result = resp.result as {
|
||||||
|
addressComponent:Address;
|
||||||
|
};
|
||||||
|
|
||||||
|
const address = _result.addressComponent.town + _result.addressComponent.road;
|
||||||
|
userStore.setAddress(address);
|
||||||
|
userStore.setDeviceAddress({ ..._result.addressComponent });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { getRequest } from "@/api/customFetch";
|
||||||
|
import { getCourseDetail, getLessonCatalog, getLessonCategory, getLessonList } from "@/api/interfaceDocument";
|
||||||
|
|
||||||
|
export const useGetLessonCategory = () => {
|
||||||
|
const lessonCategory = ref<any[]>([]);
|
||||||
|
getRequest(getLessonCategory()).then(resp => {
|
||||||
|
if(resp.code === 200){
|
||||||
|
lessonCategory.value = (resp.result as {categories:any[]}).categories;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return { lessonCategory }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useGetLessonList = () => {
|
||||||
|
const lessonList = ref<any[]>([]);
|
||||||
|
getRequest(getLessonList()).then(resp => {
|
||||||
|
if(resp.code === 200){
|
||||||
|
lessonList.value = (resp.result as {items:any[]}).items;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return { lessonList }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useGetCourseDetail = () => {
|
||||||
|
const courseDetail = ref<any>({});
|
||||||
|
getRequest(getCourseDetail()).then(resp => {
|
||||||
|
if(resp.code === 200){
|
||||||
|
courseDetail.value = resp.result;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return { courseDetail }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useGetCourseCatalog = () => {
|
||||||
|
const courseCatalog = ref<any[]>([]);
|
||||||
|
getRequest(getLessonCatalog()).then(resp => {
|
||||||
|
if(resp.code === 200){
|
||||||
|
courseCatalog.value = (resp.result as {chapters:any[]}).chapters;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return { courseCatalog }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { getRequest } from "@/api/customFetch";
|
||||||
|
import { getServiceDetail } from "@/api/interfaceDocument";
|
||||||
|
|
||||||
|
export const useGetServiceDetail = () => {
|
||||||
|
const serviceDetail = ref<any>(null);
|
||||||
|
getRequest(getServiceDetail()).then(res => {
|
||||||
|
serviceDetail.value = res.result;
|
||||||
|
});
|
||||||
|
|
||||||
|
return { serviceDetail };
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { getHousekeepingCleaningList,getHousekeepingCleaningDetail } from "@/api/interfaceDocument";
|
||||||
|
import { getRequest } from "@/api/customFetch";
|
||||||
|
|
||||||
|
export const useGetHousekeepingCleaningList = () => {
|
||||||
|
const cleaningList = ref<any[]>([]);
|
||||||
|
getRequest(getHousekeepingCleaningList()).then((res) => {
|
||||||
|
cleaningList.value = res.result as any[]
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
cleaningList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useGetHousekeepingCleaningDetail = () => {
|
||||||
|
const cleaningDetail = ref<any>({});
|
||||||
|
getRequest(getHousekeepingCleaningDetail()).then((res) => {
|
||||||
|
cleaningDetail.value = res.result as any
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
cleaningDetail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { getRequest } from "@/api/customFetch"
|
||||||
|
import { getNotifyList } from "@/api/interfaceDocument"
|
||||||
|
|
||||||
|
|
||||||
|
export const useGetNoticeList = () => {
|
||||||
|
const noticeList = ref<any[]>([])
|
||||||
|
getRequest(getNotifyList()).then(resp => {
|
||||||
|
if(resp.code === 200){
|
||||||
|
noticeList.value = (resp.result as {list:any[]}).list
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return { noticeList }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { getNursingHomeList, getNursingDetail } from "@/api/interfaceDocument";
|
||||||
|
import { getRequest } from "@/api/customFetch";
|
||||||
|
|
||||||
|
export const useNursingHomeList = () => {
|
||||||
|
const nursingHomeList = ref<any[]>([]);
|
||||||
|
getRequest(getNursingHomeList()).then(resp => {
|
||||||
|
if(resp.code === 200){
|
||||||
|
nursingHomeList.value = (resp.result as { list: any[] }).list;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {nursingHomeList}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useNursingDetail = () => {
|
||||||
|
const nursingDetail = ref<any>({});
|
||||||
|
getRequest(getNursingDetail()).then(resp => {
|
||||||
|
if(resp.code === 200){
|
||||||
|
nursingDetail.value = (resp.result as { data: any }).data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return { nursingDetail };
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { getRequest } from "@/api/customFetch";
|
||||||
|
import { getRestaurantFoodDetail, getRestaurantFoodList, getRestaurantFoodSet } from "@/api/interfaceDocument";
|
||||||
|
|
||||||
|
export const foodList = ref<any[]>([]);
|
||||||
|
export const packingFee = ref(0);
|
||||||
|
export const shippingFee = ref(0);
|
||||||
|
export const useRestaurantFoodList = () => {
|
||||||
|
|
||||||
|
getRequest(getRestaurantFoodList()).then((resp) => {
|
||||||
|
if (resp.code === 200) {
|
||||||
|
foodList.value = (resp.result as { items: any[] }).items;
|
||||||
|
packingFee.value = (resp.result as { packingFee: number }).packingFee;
|
||||||
|
shippingFee.value = (resp.result as { shippingFee: number }).shippingFee;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const foodSetList = ref<any[]>([]);
|
||||||
|
export const useRestaurantFoodSet = () => {
|
||||||
|
getRequest(getRestaurantFoodSet()).then((resp) => {
|
||||||
|
if (resp.code === 200) {
|
||||||
|
foodSetList.value = (resp.result as { items: any[] }).items;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const useRestaurantFoodDetail = () => {
|
||||||
|
const foodDetail = ref<any>({});
|
||||||
|
|
||||||
|
getRequest(getRestaurantFoodDetail()).then((resp) => {
|
||||||
|
if (resp.code === 200) {
|
||||||
|
foodDetail.value = resp.result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {foodDetail}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { getRequest } from "@/api/customFetch";
|
||||||
|
import { getTravelList } from "@/api/interfaceDocument";
|
||||||
|
|
||||||
|
export const useTravelList = () => {
|
||||||
|
const travelList = ref<any[]>([]);
|
||||||
|
const travelCategory = ref<any[]>([]);
|
||||||
|
|
||||||
|
getRequest(getTravelList()).then((resp) => {
|
||||||
|
if (resp.code === 200) {
|
||||||
|
travelList.value = (resp.result as { data:{products: any[]} }).data.products;
|
||||||
|
travelCategory.value = (resp.result as {data: {categories: any[]} }).data.categories.map((item,index) => ({title:item,paneKey:index.toString()}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { travelList,travelCategory };
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { getUserAddressList } from "@/api/interfaceDocument"
|
||||||
|
import { getRequest } from "@/api/customFetch"
|
||||||
|
import { useUserStore } from "@/store/user"
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
export const address = ref<any[]>([])
|
||||||
|
export const defaultAddress = ref(1);
|
||||||
|
|
||||||
|
export const useMatchDefault = () => {
|
||||||
|
const matchAddress = address.value.find(item => item.isDefault);
|
||||||
|
if(matchAddress){
|
||||||
|
defaultAddress.value = matchAddress.id
|
||||||
|
}else{
|
||||||
|
defaultAddress.value = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useGetUserAddressList = () => {
|
||||||
|
getRequest(getUserAddressList()).then(resp => {
|
||||||
|
if(resp.code === 200){
|
||||||
|
address.value = (resp.result as {list:any[]}).list
|
||||||
|
useMatchDefault()
|
||||||
|
userStore.setRecvAddrList(address.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { createApp } from 'vue'
|
||||||
|
import '@unocss/reset/tailwind-compat.css'
|
||||||
|
import './style.css'
|
||||||
|
import 'uno.css';
|
||||||
|
import App from './App.vue'
|
||||||
|
// Pinia 持久化
|
||||||
|
import store from '@/store/index'
|
||||||
|
//导入路由
|
||||||
|
import router from '@/router/index'
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
|
||||||
|
// 注册已经加上了持久化的pinia
|
||||||
|
app.use(store).use(router)
|
||||||
|
|
||||||
|
app.mount('#app')
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { createRouter, createWebHistory } from "vue-router";
|
||||||
|
import { publicRoutes } from "./publicRoutes";
|
||||||
|
import { privateRoutes } from "./privateRoutes";
|
||||||
|
function getRoutes() {
|
||||||
|
const routes = [ // 私有路由,请在这里添加
|
||||||
|
...privateRoutes,
|
||||||
|
|
||||||
|
// 公共路由
|
||||||
|
...publicRoutes,
|
||||||
|
];
|
||||||
|
/**
|
||||||
|
* 如果要对 routes 做一些处理,请在这里修改
|
||||||
|
*/
|
||||||
|
return routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes: getRoutes(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// 全局前置守卫,这边可以对身份进行验证
|
||||||
|
router.beforeEach((to, _from, next) => {
|
||||||
|
let userRole = "admin";
|
||||||
|
// 如果目标路由没有角色限制
|
||||||
|
if (!to.meta.role) {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
// 判断当前用户角色是否在目标路由的允许角色列表中
|
||||||
|
if ((to.meta.role as string[]).includes(userRole)) {
|
||||||
|
// 如果角色匹配,允许进入目标路由
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
// 如果角色不匹配,跳转到 unauthorized 页面
|
||||||
|
next({ path: "/unauthorized" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听路由变化,动态设置网页标题
|
||||||
|
router.afterEach((to) => {
|
||||||
|
if (to.meta.title) {
|
||||||
|
document.title = to.meta.title as string;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { ComponentInternalInstance, ref } from 'vue'
|
||||||
|
|
||||||
|
export const excludes = ref<string[]>([])
|
||||||
|
|
||||||
|
export function removeKeepAliveCache(instance: ComponentInternalInstance) {
|
||||||
|
if (!excludes.value.includes(instance.type.name!)) {
|
||||||
|
excludes.value.push(instance.type.name!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resetKeepAliveCache(instance: ComponentInternalInstance) {
|
||||||
|
excludes.value = excludes.value.filter((item) => item !== instance.type.name)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { RouteRecordRaw } from "vue-router";
|
||||||
|
|
||||||
|
export const privateRoutes:RouteRecordRaw[] = [];
|
||||||
|
|
@ -0,0 +1,317 @@
|
||||||
|
import { RouteRecordRaw } from "vue-router";
|
||||||
|
|
||||||
|
export const publicRoutes:RouteRecordRaw[] = [
|
||||||
|
{
|
||||||
|
path: "/home",
|
||||||
|
name: "home",
|
||||||
|
meta: {
|
||||||
|
title: "康乐云家",
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
default: () => import("../views/home/index.vue"),
|
||||||
|
tabbar: () => import("../components/tabbar/index.vue")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/location/map",
|
||||||
|
name: "map",
|
||||||
|
meta: {
|
||||||
|
title: "新增地址",
|
||||||
|
},
|
||||||
|
components: {default:() => import("../views/location/map.vue"),backButton:() => import("../components/back-button/index.vue")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/location/administrative",
|
||||||
|
name: "administrative",
|
||||||
|
meta: {
|
||||||
|
title: "选择城市",
|
||||||
|
},
|
||||||
|
components: {default:() => import("../views/location/administrative.vue")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/medical",
|
||||||
|
name: "medical",
|
||||||
|
meta: {
|
||||||
|
title: "康乐云家",
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
default: () => import("../views/medical/index.vue"),
|
||||||
|
tabbar: () => import("../components/tabbar/index.vue")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/shop",
|
||||||
|
name: "shop",
|
||||||
|
meta: {
|
||||||
|
title: "康乐云家",
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
default: () => import("../views/shop/index.vue"),
|
||||||
|
tabbar: () => import("../components/tabbar/index.vue")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/shop/product",
|
||||||
|
name: "product",
|
||||||
|
meta: {
|
||||||
|
title: "商品详情",
|
||||||
|
},
|
||||||
|
components: {default:() => import("../views/shop/productInfo.vue"),tabbar: () => import("../components/back-button/index.vue")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/checkout",
|
||||||
|
name: "checkout",
|
||||||
|
meta: {
|
||||||
|
title: "确认订单",
|
||||||
|
},
|
||||||
|
components: {default:() => import("../views/checkout/index.vue"),backButton:() => import("../components/back-button/index.vue")},
|
||||||
|
props:{
|
||||||
|
backButton:{bottom:"15vh"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/checkout/payment",
|
||||||
|
name: "payment",
|
||||||
|
meta: {
|
||||||
|
title: "支付成功",
|
||||||
|
},
|
||||||
|
component: () => import("../views/checkout/payment.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:"/checkout/health-care",
|
||||||
|
name:"health-care-checkout",
|
||||||
|
meta:{
|
||||||
|
title:"确认订单"
|
||||||
|
},
|
||||||
|
components:{default:() => import("../views/checkout/healthCare.vue"),backButton:() => import("../components/back-button/index.vue")},
|
||||||
|
props:{
|
||||||
|
backButton:{bottom:"15vh"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:"/housekeeping/order/:id",
|
||||||
|
name:"housekeeping-checkout",
|
||||||
|
meta:{
|
||||||
|
title:"确认订单"
|
||||||
|
},
|
||||||
|
components:{default:() => import("../views/housekeeping/order.vue"),backButton:() => import("../components/back-button/index.vue")},
|
||||||
|
props:{
|
||||||
|
backButton:{bottom:"15vh"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
path: "/mine",
|
||||||
|
name: "mine",
|
||||||
|
meta: {
|
||||||
|
title: "康乐云家",
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
default: () => import("../views/mine/index.vue"),
|
||||||
|
tabbar: () => import("../components/tabbar/index.vue")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'/mine/address',
|
||||||
|
name:'my-address',
|
||||||
|
meta:{
|
||||||
|
title:'收获地址管理'
|
||||||
|
},
|
||||||
|
component: () => import("../views/mine/addressManagement.vue")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'/mine/add/address',
|
||||||
|
name:'add-address',
|
||||||
|
meta:{
|
||||||
|
title:'收获地址管理'
|
||||||
|
},
|
||||||
|
component: () => import("../views/mine/addAddress.vue")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'/mine/add/paramedic',
|
||||||
|
name:'add-paramedic',
|
||||||
|
meta:{
|
||||||
|
title:'新增被护理人'
|
||||||
|
},
|
||||||
|
component: () => import("../views/mine/addParamedic.vue")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'/mine/notice',
|
||||||
|
name:'notice',
|
||||||
|
meta:{
|
||||||
|
title:'消息中心'
|
||||||
|
},
|
||||||
|
components: {default:() => import("../views/mine/notice.vue"),backButton:() => import("../components/back-button/index.vue")},
|
||||||
|
props:{
|
||||||
|
backButton:{bottom:"15vh"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'/mine/orders',
|
||||||
|
name:"mine-orders",
|
||||||
|
meta:{
|
||||||
|
title:'我的订单'
|
||||||
|
},
|
||||||
|
components: {default:() => import("../views/mine/orders.vue"),backButton:() => import("../components/back-button/index.vue")},
|
||||||
|
props:{
|
||||||
|
backButton:{bottom:"15vh"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'/mine/user-info',
|
||||||
|
name:'mine-user-info',
|
||||||
|
meta:{
|
||||||
|
title:'个人资料'
|
||||||
|
},
|
||||||
|
component: () => import("../views/mine/userInfo.vue")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'/restaurant',
|
||||||
|
name:'restaurant',
|
||||||
|
meta:{
|
||||||
|
title:'社区点餐'
|
||||||
|
},
|
||||||
|
components: {default:() => import("../views/restaurant/index.vue"),backButton:() => import("../components/back-button/index.vue")},
|
||||||
|
props:{
|
||||||
|
backButton:{bottom:"15vh"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:"/restaurant/detail/:id",
|
||||||
|
name:"restaurant-detail",
|
||||||
|
meta:{
|
||||||
|
title:"商品详情"
|
||||||
|
},
|
||||||
|
components: {default:() => import("../views/restaurant/detail.vue"),backButton:() => import("../components/back-button/index.vue")}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'/courses',
|
||||||
|
name:'courses',
|
||||||
|
meta:{
|
||||||
|
title:'课程学习'
|
||||||
|
},
|
||||||
|
components: {default:() => import("../views/courses/index.vue"),backButton:() => import("../components/back-button/index.vue")},
|
||||||
|
props:{
|
||||||
|
backButton:{bottom:"15vh"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'/courses/detail/:id',
|
||||||
|
name:'courses-detail',
|
||||||
|
meta:{
|
||||||
|
title:'课程详情'
|
||||||
|
},
|
||||||
|
components: {default:() => import("../views/courses/detail.vue"),backButton:() => import("../components/back-button/index.vue")}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'/courses/cart/:id',
|
||||||
|
name:'courses-cart',
|
||||||
|
meta:{
|
||||||
|
title:'购买课程'
|
||||||
|
},
|
||||||
|
component: () => import("../views/courses/courseCart.vue")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'/travel',
|
||||||
|
name:'travel',
|
||||||
|
meta:{
|
||||||
|
title:'乐享夕阳行'
|
||||||
|
},
|
||||||
|
components: {default:() => import("../views/travel/index.vue"),backButton:() => import("../components/back-button/index.vue")},
|
||||||
|
props:{
|
||||||
|
backButton:{bottom:"15vh"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'/health-care',
|
||||||
|
name:'health-care',
|
||||||
|
meta:{
|
||||||
|
title:'康养护理'
|
||||||
|
},
|
||||||
|
components: {default:() => import("../views/healthCare/index.vue"),backButton:() => import("../components/back-button/index.vue")},
|
||||||
|
props:{
|
||||||
|
backButton:{bottom:"15vh"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'/health-care/order/:id',
|
||||||
|
name:'health-care-order',
|
||||||
|
meta:{
|
||||||
|
title:'预约'
|
||||||
|
},
|
||||||
|
components: {default:() => import("../views/healthCare/order.vue"),backButton:() => import("../components/back-button/index.vue")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'/jinze',
|
||||||
|
name:'jinze',
|
||||||
|
meta:{
|
||||||
|
title:'锦泽安康'
|
||||||
|
},
|
||||||
|
components: {default:() => import("../views/jinze/index.vue"),backButton:() => import("../components/back-button/index.vue")},
|
||||||
|
props:{
|
||||||
|
backButton:{bottom:"15vh"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'/jinze/detail/:id',
|
||||||
|
name:'jinze-detail',
|
||||||
|
meta:{
|
||||||
|
title:'养老院详情'
|
||||||
|
},
|
||||||
|
components: {default:() => import("../views/jinze/detail.vue"),backButton:() => import("../components/back-button/index.vue")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'/jinze/images/:id',
|
||||||
|
name:'jinze-images',
|
||||||
|
meta:{
|
||||||
|
title:'泰康之家·锦绣府'
|
||||||
|
},
|
||||||
|
components: {default:() => import("../views/jinze/images.vue"),backButton:() => import("../components/back-button/index.vue")},
|
||||||
|
props:{
|
||||||
|
backButton:{bottom:"15vh"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'/jinze/patient-situation',
|
||||||
|
name:'jinze-patient-situation',
|
||||||
|
meta:{
|
||||||
|
title:'情况描述'
|
||||||
|
},
|
||||||
|
component: () => import("../views/jinze/patientSituation.vue")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'/home/address',
|
||||||
|
name:'current-address',
|
||||||
|
meta:{
|
||||||
|
title:'选择定位'
|
||||||
|
},
|
||||||
|
component: () => import("../views/home/currentLocation.vue")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'/housekeeping',
|
||||||
|
name:'housekeeping',
|
||||||
|
meta:{
|
||||||
|
title:'预约家政'
|
||||||
|
},
|
||||||
|
components: {default:() => import("../views/housekeeping/index.vue"),backButton:() => import("../components/back-button/index.vue")},
|
||||||
|
props:{
|
||||||
|
backButton:{bottom:"15vh"}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:"/housekeeping/detail/:id",
|
||||||
|
name:"housekeeping-detail",
|
||||||
|
meta:{
|
||||||
|
title:"家政详情"
|
||||||
|
},
|
||||||
|
components: {default:() => import("../views/housekeeping/detail.vue"),backButton:() => import("../components/back-button/index.vue")},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/unauthorized",
|
||||||
|
name: "unauthorized",
|
||||||
|
meta: {
|
||||||
|
title: "unauthorized",
|
||||||
|
},
|
||||||
|
component: () => import("../views/unauthorized.vue"),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
// pinia数据持久化存储
|
||||||
|
import { createPinia } from 'pinia'
|
||||||
|
import { createPersistedState } from 'pinia-plugin-persistedstate'
|
||||||
|
import { SelfStorage } from './secureStore'
|
||||||
|
|
||||||
|
// 第一个参数是应用程序中 store 的唯一 id
|
||||||
|
const store = createPinia()
|
||||||
|
store.use(
|
||||||
|
createPersistedState({
|
||||||
|
storage: SelfStorage
|
||||||
|
})
|
||||||
|
)
|
||||||
|
export default store
|
||||||