feat:搭建基本框架
This commit is contained in:
@@ -2,9 +2,9 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<link rel="icon" type="image/svg+xml" href="/logo.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + Vue + TS</title>
|
||||
<title>食光集</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc -b && vite build",
|
||||
"dev": "vite --host 0.0.0.0",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
327
pnpm-lock.yaml
generated
327
pnpm-lock.yaml
generated
@@ -8,12 +8,18 @@ importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
'@vueuse/core':
|
||||
specifier: ^13.1.0
|
||||
version: 13.4.0(vue@3.5.17(typescript@5.8.3))
|
||||
element-plus:
|
||||
specifier: ^2.9.7
|
||||
version: 2.10.2(vue@3.5.17(typescript@5.8.3))
|
||||
vue:
|
||||
specifier: ^3.5.13
|
||||
version: 3.5.17(typescript@5.8.3)
|
||||
vue3-common:
|
||||
specifier: git+https://gitee.com/Cxx0822/vue3-common
|
||||
version: git+https://gitee.com/Cxx0822/vue3-common#ba7c8a0b1a20a714f0bf09b10d9486b5fd518ca0(axios@1.10.0)(crypto-js@4.2.0)(echarts@5.6.0)(element-plus@2.10.2(vue@3.5.17(typescript@5.8.3)))(font-awesome@4.7.0)(js-cookie@3.0.5)(path-browserify@1.0.1)(pinia@2.3.1(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3)))(qs@6.14.0)(vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3))
|
||||
version: git+https://gitee.com/Cxx0822/vue3-common#4cabccc268f404bef9fd13078ff88fc2830b7461(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3))(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-config-standard@16.0.3(eslint-plugin-import@2.32.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0))(eslint-plugin-node@11.1.0(eslint@7.32.0))(eslint-plugin-promise@5.2.0(eslint@7.32.0))(eslint@7.32.0))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0))(eslint-plugin-node@11.1.0(eslint@7.32.0))(eslint-plugin-promise@5.2.0(eslint@7.32.0))(eslint-plugin-vue@8.7.1(eslint@7.32.0))(eslint@7.32.0)(pinia@2.3.1(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3)))(vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3))
|
||||
vue3-emoji-picker:
|
||||
specifier: ^1.1.8
|
||||
version: 1.1.8(typescript@5.8.3)
|
||||
@@ -29,7 +35,7 @@ importers:
|
||||
version: 5.62.0(eslint@7.32.0)(typescript@5.8.3)
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: ^5.2.3
|
||||
version: 5.2.4(vite@6.3.5(@types/node@18.19.112))(vue@3.5.17(typescript@5.8.3))
|
||||
version: 5.2.4(vite@6.3.5(@types/node@18.19.112)(sass@1.89.2))(vue@3.5.17(typescript@5.8.3))
|
||||
'@vue/tsconfig':
|
||||
specifier: ^0.7.0
|
||||
version: 0.7.0(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3))
|
||||
@@ -51,12 +57,15 @@ importers:
|
||||
eslint-plugin-vue:
|
||||
specifier: ^8.7.1
|
||||
version: 8.7.1(eslint@7.32.0)
|
||||
sass:
|
||||
specifier: ^1.86.3
|
||||
version: 1.89.2
|
||||
typescript:
|
||||
specifier: ~5.8.3
|
||||
version: 5.8.3
|
||||
vite:
|
||||
specifier: ^6.3.5
|
||||
version: 6.3.5(@types/node@18.19.112)
|
||||
version: 6.3.5(@types/node@18.19.112)(sass@1.89.2)
|
||||
vue-tsc:
|
||||
specifier: ^2.2.8
|
||||
version: 2.2.10(typescript@5.8.3)
|
||||
@@ -301,6 +310,94 @@ packages:
|
||||
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
'@parcel/watcher-android-arm64@2.5.1':
|
||||
resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@parcel/watcher-darwin-arm64@2.5.1':
|
||||
resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@parcel/watcher-darwin-x64@2.5.1':
|
||||
resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@parcel/watcher-freebsd-x64@2.5.1':
|
||||
resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@parcel/watcher-linux-arm-glibc@2.5.1':
|
||||
resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-arm-musl@2.5.1':
|
||||
resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-linux-arm64-glibc@2.5.1':
|
||||
resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-arm64-musl@2.5.1':
|
||||
resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-linux-x64-glibc@2.5.1':
|
||||
resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-x64-musl@2.5.1':
|
||||
resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-win32-arm64@2.5.1':
|
||||
resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@parcel/watcher-win32-ia32@2.5.1':
|
||||
resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@parcel/watcher-win32-x64@2.5.1':
|
||||
resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@parcel/watcher@2.5.1':
|
||||
resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
|
||||
'@popperjs/core@2.11.8':
|
||||
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
||||
|
||||
@@ -448,6 +545,9 @@ packages:
|
||||
'@types/web-bluetooth@0.0.16':
|
||||
resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
|
||||
|
||||
'@types/web-bluetooth@0.0.21':
|
||||
resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@5.62.0':
|
||||
resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
@@ -576,12 +676,25 @@ packages:
|
||||
vue:
|
||||
optional: true
|
||||
|
||||
'@vueuse/core@13.4.0':
|
||||
resolution: {integrity: sha512-OnK7zW3bTq/QclEk17+vDFN3tuAm8ONb9zQUIHrYQkkFesu3WeGUx/3YzpEp+ly53IfDAT9rsYXgGW6piNZC5w==}
|
||||
peerDependencies:
|
||||
vue: ^3.5.0
|
||||
|
||||
'@vueuse/core@9.13.0':
|
||||
resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==}
|
||||
|
||||
'@vueuse/metadata@13.4.0':
|
||||
resolution: {integrity: sha512-CPDQ/IgOeWbqItg1c/pS+Ulum63MNbpJ4eecjFJqgD/JUCJ822zLfpw6M9HzSvL6wbzMieOtIAW/H8deQASKHg==}
|
||||
|
||||
'@vueuse/metadata@9.13.0':
|
||||
resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==}
|
||||
|
||||
'@vueuse/shared@13.4.0':
|
||||
resolution: {integrity: sha512-+AxuKbw8R1gYy5T21V5yhadeNM7rJqb4cPaRI9DdGnnNl3uqXh+unvQ3uCaA2DjYLbNr1+l7ht/B4qEsRegX6A==}
|
||||
peerDependencies:
|
||||
vue: ^3.5.0
|
||||
|
||||
'@vueuse/shared@9.13.0':
|
||||
resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==}
|
||||
|
||||
@@ -720,6 +833,10 @@ packages:
|
||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
chokidar@4.0.3:
|
||||
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
||||
engines: {node: '>= 14.16.0'}
|
||||
|
||||
color-convert@1.9.3:
|
||||
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
|
||||
|
||||
@@ -817,6 +934,11 @@ packages:
|
||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
|
||||
detect-libc@1.0.3:
|
||||
resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
|
||||
engines: {node: '>=0.10'}
|
||||
hasBin: true
|
||||
|
||||
dir-glob@3.0.1:
|
||||
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -1085,10 +1207,6 @@ packages:
|
||||
debug:
|
||||
optional: true
|
||||
|
||||
font-awesome@4.7.0:
|
||||
resolution: {integrity: sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==}
|
||||
engines: {node: '>=0.10.3'}
|
||||
|
||||
for-each@0.3.5:
|
||||
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -1157,10 +1275,6 @@ packages:
|
||||
graphemer@1.4.0:
|
||||
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
|
||||
|
||||
happy-dom@16.8.1:
|
||||
resolution: {integrity: sha512-n0QrmT9lD81rbpKsyhnlz3DgnMZlaOkJPpgi746doA+HvaMC79bdWkwjrNnGJRvDrWTI8iOcJiVTJ5CdT/AZRw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
has-bigints@1.1.0:
|
||||
resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -1207,6 +1321,9 @@ packages:
|
||||
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
immutable@5.1.3:
|
||||
resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==}
|
||||
|
||||
import-fresh@3.3.1:
|
||||
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -1452,6 +1569,9 @@ packages:
|
||||
natural-compare@1.4.0:
|
||||
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
||||
|
||||
node-addon-api@7.1.1:
|
||||
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
||||
|
||||
normalize-wheel-es@1.2.0:
|
||||
resolution: {integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==}
|
||||
|
||||
@@ -1569,6 +1689,10 @@ packages:
|
||||
queue-microtask@1.2.3:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||
|
||||
readdirp@4.1.2:
|
||||
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
|
||||
engines: {node: '>= 14.18.0'}
|
||||
|
||||
reflect.getprototypeof@1.0.10:
|
||||
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -1623,6 +1747,11 @@ packages:
|
||||
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
sass@1.89.2:
|
||||
resolution: {integrity: sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
hasBin: true
|
||||
|
||||
semver@6.3.1:
|
||||
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
||||
hasBin: true
|
||||
@@ -1884,19 +2013,11 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: '>=5.0.0'
|
||||
|
||||
vue3-common@git+https://gitee.com/Cxx0822/vue3-common#ba7c8a0b1a20a714f0bf09b10d9486b5fd518ca0:
|
||||
resolution: {commit: ba7c8a0b1a20a714f0bf09b10d9486b5fd518ca0, repo: https://gitee.com/Cxx0822/vue3-common, type: git}
|
||||
vue3-common@git+https://gitee.com/Cxx0822/vue3-common#4cabccc268f404bef9fd13078ff88fc2830b7461:
|
||||
resolution: {commit: 4cabccc268f404bef9fd13078ff88fc2830b7461, repo: https://gitee.com/Cxx0822/vue3-common, type: git}
|
||||
version: 1.0.0
|
||||
peerDependencies:
|
||||
axios: ^1.4.0 || 0.27.2
|
||||
crypto-js: ^4.2.0
|
||||
echarts: ^5.5.1
|
||||
element-plus: ^2.6.0
|
||||
font-awesome: ^4.7.0
|
||||
js-cookie: ^3.0.5
|
||||
path-browserify: ^1.0.1
|
||||
pinia: ^2.3.0
|
||||
qs: ^6.13.0
|
||||
vue: ^3.5.12
|
||||
vue-router: ^4.2.2
|
||||
|
||||
@@ -1904,6 +2025,19 @@ packages:
|
||||
resolution: {integrity: sha512-k9tVHeQEBVLzVCLYAkFaI1nib3FJFQwdPhWD5khJkhks3ktg3g12z5wPGOSDpIuSLNtelRGvq1qdmZuJu5khfA==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
|
||||
vue3-eslint@git+https://gitee.com/Cxx0822/vue3-eslint#849a4e0e783d3ecc57035fb2f92950998b6b2e41:
|
||||
resolution: {commit: 849a4e0e783d3ecc57035fb2f92950998b6b2e41, repo: https://gitee.com/Cxx0822/vue3-eslint, type: git}
|
||||
version: 1.0.0
|
||||
peerDependencies:
|
||||
'@typescript-eslint/eslint-plugin': ^5.40.1
|
||||
'@typescript-eslint/parser': ^5.40.1
|
||||
eslint: ^7.32.0
|
||||
eslint-config-standard: ^16.0.3
|
||||
eslint-plugin-import: ^2.25.4
|
||||
eslint-plugin-node: ^11.1.0
|
||||
eslint-plugin-promise: ^5.2.0
|
||||
eslint-plugin-vue: ^8.5.0
|
||||
|
||||
vue@3.5.17:
|
||||
resolution: {integrity: sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==}
|
||||
peerDependencies:
|
||||
@@ -1912,14 +2046,6 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
webidl-conversions@7.0.0:
|
||||
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
whatwg-mimetype@3.0.0:
|
||||
resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
which-boxed-primitive@1.1.1:
|
||||
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -2118,6 +2244,67 @@ snapshots:
|
||||
'@nodelib/fs.scandir': 2.1.5
|
||||
fastq: 1.19.1
|
||||
|
||||
'@parcel/watcher-android-arm64@2.5.1':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-darwin-arm64@2.5.1':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-darwin-x64@2.5.1':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-freebsd-x64@2.5.1':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-linux-arm-glibc@2.5.1':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-linux-arm-musl@2.5.1':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-linux-arm64-glibc@2.5.1':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-linux-arm64-musl@2.5.1':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-linux-x64-glibc@2.5.1':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-linux-x64-musl@2.5.1':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-win32-arm64@2.5.1':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-win32-ia32@2.5.1':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher-win32-x64@2.5.1':
|
||||
optional: true
|
||||
|
||||
'@parcel/watcher@2.5.1':
|
||||
dependencies:
|
||||
detect-libc: 1.0.3
|
||||
is-glob: 4.0.3
|
||||
micromatch: 4.0.8
|
||||
node-addon-api: 7.1.1
|
||||
optionalDependencies:
|
||||
'@parcel/watcher-android-arm64': 2.5.1
|
||||
'@parcel/watcher-darwin-arm64': 2.5.1
|
||||
'@parcel/watcher-darwin-x64': 2.5.1
|
||||
'@parcel/watcher-freebsd-x64': 2.5.1
|
||||
'@parcel/watcher-linux-arm-glibc': 2.5.1
|
||||
'@parcel/watcher-linux-arm-musl': 2.5.1
|
||||
'@parcel/watcher-linux-arm64-glibc': 2.5.1
|
||||
'@parcel/watcher-linux-arm64-musl': 2.5.1
|
||||
'@parcel/watcher-linux-x64-glibc': 2.5.1
|
||||
'@parcel/watcher-linux-x64-musl': 2.5.1
|
||||
'@parcel/watcher-win32-arm64': 2.5.1
|
||||
'@parcel/watcher-win32-ia32': 2.5.1
|
||||
'@parcel/watcher-win32-x64': 2.5.1
|
||||
optional: true
|
||||
|
||||
'@popperjs/core@2.11.8': {}
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.44.0':
|
||||
@@ -2206,6 +2393,8 @@ snapshots:
|
||||
|
||||
'@types/web-bluetooth@0.0.16': {}
|
||||
|
||||
'@types/web-bluetooth@0.0.21': {}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.12.1
|
||||
@@ -2290,9 +2479,9 @@ snapshots:
|
||||
'@typescript-eslint/types': 5.62.0
|
||||
eslint-visitor-keys: 3.4.3
|
||||
|
||||
'@vitejs/plugin-vue@5.2.4(vite@6.3.5(@types/node@18.19.112))(vue@3.5.17(typescript@5.8.3))':
|
||||
'@vitejs/plugin-vue@5.2.4(vite@6.3.5(@types/node@18.19.112)(sass@1.89.2))(vue@3.5.17(typescript@5.8.3))':
|
||||
dependencies:
|
||||
vite: 6.3.5(@types/node@18.19.112)
|
||||
vite: 6.3.5(@types/node@18.19.112)(sass@1.89.2)
|
||||
vue: 3.5.17(typescript@5.8.3)
|
||||
|
||||
'@volar/language-core@2.4.14':
|
||||
@@ -2386,6 +2575,13 @@ snapshots:
|
||||
typescript: 5.8.3
|
||||
vue: 3.5.17(typescript@5.8.3)
|
||||
|
||||
'@vueuse/core@13.4.0(vue@3.5.17(typescript@5.8.3))':
|
||||
dependencies:
|
||||
'@types/web-bluetooth': 0.0.21
|
||||
'@vueuse/metadata': 13.4.0
|
||||
'@vueuse/shared': 13.4.0(vue@3.5.17(typescript@5.8.3))
|
||||
vue: 3.5.17(typescript@5.8.3)
|
||||
|
||||
'@vueuse/core@9.13.0(vue@3.5.17(typescript@5.8.3))':
|
||||
dependencies:
|
||||
'@types/web-bluetooth': 0.0.16
|
||||
@@ -2396,8 +2592,14 @@ snapshots:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
|
||||
'@vueuse/metadata@13.4.0': {}
|
||||
|
||||
'@vueuse/metadata@9.13.0': {}
|
||||
|
||||
'@vueuse/shared@13.4.0(vue@3.5.17(typescript@5.8.3))':
|
||||
dependencies:
|
||||
vue: 3.5.17(typescript@5.8.3)
|
||||
|
||||
'@vueuse/shared@9.13.0(vue@3.5.17(typescript@5.8.3))':
|
||||
dependencies:
|
||||
vue-demi: 0.14.10(vue@3.5.17(typescript@5.8.3))
|
||||
@@ -2570,6 +2772,10 @@ snapshots:
|
||||
ansi-styles: 4.3.0
|
||||
supports-color: 7.2.0
|
||||
|
||||
chokidar@4.0.3:
|
||||
dependencies:
|
||||
readdirp: 4.1.2
|
||||
|
||||
color-convert@1.9.3:
|
||||
dependencies:
|
||||
color-name: 1.1.3
|
||||
@@ -2659,6 +2865,9 @@ snapshots:
|
||||
|
||||
delayed-stream@1.0.0: {}
|
||||
|
||||
detect-libc@1.0.3:
|
||||
optional: true
|
||||
|
||||
dir-glob@3.0.1:
|
||||
dependencies:
|
||||
path-type: 4.0.0
|
||||
@@ -3056,8 +3265,6 @@ snapshots:
|
||||
|
||||
follow-redirects@1.15.9: {}
|
||||
|
||||
font-awesome@4.7.0: {}
|
||||
|
||||
for-each@0.3.5:
|
||||
dependencies:
|
||||
is-callable: 1.2.7
|
||||
@@ -3149,11 +3356,6 @@ snapshots:
|
||||
|
||||
graphemer@1.4.0: {}
|
||||
|
||||
happy-dom@16.8.1:
|
||||
dependencies:
|
||||
webidl-conversions: 7.0.0
|
||||
whatwg-mimetype: 3.0.0
|
||||
|
||||
has-bigints@1.1.0: {}
|
||||
|
||||
has-flag@3.0.0: {}
|
||||
@@ -3186,6 +3388,8 @@ snapshots:
|
||||
|
||||
ignore@5.3.2: {}
|
||||
|
||||
immutable@5.1.3: {}
|
||||
|
||||
import-fresh@3.3.1:
|
||||
dependencies:
|
||||
parent-module: 1.0.1
|
||||
@@ -3410,6 +3614,9 @@ snapshots:
|
||||
|
||||
natural-compare@1.4.0: {}
|
||||
|
||||
node-addon-api@7.1.1:
|
||||
optional: true
|
||||
|
||||
normalize-wheel-es@1.2.0: {}
|
||||
|
||||
nth-check@2.1.1:
|
||||
@@ -3525,6 +3732,8 @@ snapshots:
|
||||
|
||||
queue-microtask@1.2.3: {}
|
||||
|
||||
readdirp@4.1.2: {}
|
||||
|
||||
reflect.getprototypeof@1.0.10:
|
||||
dependencies:
|
||||
call-bind: 1.0.8
|
||||
@@ -3612,6 +3821,14 @@ snapshots:
|
||||
es-errors: 1.3.0
|
||||
is-regex: 1.2.1
|
||||
|
||||
sass@1.89.2:
|
||||
dependencies:
|
||||
chokidar: 4.0.3
|
||||
immutable: 5.1.3
|
||||
source-map-js: 1.2.1
|
||||
optionalDependencies:
|
||||
'@parcel/watcher': 2.5.1
|
||||
|
||||
semver@6.3.1: {}
|
||||
|
||||
semver@7.7.2: {}
|
||||
@@ -3840,7 +4057,7 @@ snapshots:
|
||||
|
||||
v8-compile-cache@2.4.0: {}
|
||||
|
||||
vite@6.3.5(@types/node@18.19.112):
|
||||
vite@6.3.5(@types/node@18.19.112)(sass@1.89.2):
|
||||
dependencies:
|
||||
esbuild: 0.25.5
|
||||
fdir: 6.4.6(picomatch@4.0.2)
|
||||
@@ -3851,6 +4068,7 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/node': 18.19.112
|
||||
fsevents: 2.3.3
|
||||
sass: 1.89.2
|
||||
|
||||
vscode-uri@3.1.0: {}
|
||||
|
||||
@@ -3886,7 +4104,7 @@ snapshots:
|
||||
'@vue/language-core': 2.2.10(typescript@5.8.3)
|
||||
typescript: 5.8.3
|
||||
|
||||
vue3-common@git+https://gitee.com/Cxx0822/vue3-common#ba7c8a0b1a20a714f0bf09b10d9486b5fd518ca0(axios@1.10.0)(crypto-js@4.2.0)(echarts@5.6.0)(element-plus@2.10.2(vue@3.5.17(typescript@5.8.3)))(font-awesome@4.7.0)(js-cookie@3.0.5)(path-browserify@1.0.1)(pinia@2.3.1(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3)))(qs@6.14.0)(vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3)):
|
||||
vue3-common@git+https://gitee.com/Cxx0822/vue3-common#4cabccc268f404bef9fd13078ff88fc2830b7461(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3))(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-config-standard@16.0.3(eslint-plugin-import@2.32.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0))(eslint-plugin-node@11.1.0(eslint@7.32.0))(eslint-plugin-promise@5.2.0(eslint@7.32.0))(eslint@7.32.0))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0))(eslint-plugin-node@11.1.0(eslint@7.32.0))(eslint-plugin-promise@5.2.0(eslint@7.32.0))(eslint-plugin-vue@8.7.1(eslint@7.32.0))(eslint@7.32.0)(pinia@2.3.1(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3)))(vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3)):
|
||||
dependencies:
|
||||
'@fortawesome/fontawesome-free': 6.7.2
|
||||
'@popperjs/core': 2.11.8
|
||||
@@ -3896,8 +4114,6 @@ snapshots:
|
||||
dayjs: 1.11.13
|
||||
echarts: 5.6.0
|
||||
element-plus: 2.10.2(vue@3.5.17(typescript@5.8.3))
|
||||
font-awesome: 4.7.0
|
||||
happy-dom: 16.8.1
|
||||
js-cookie: 3.0.5
|
||||
lunar-calendar: 0.1.4
|
||||
lunar-javascript: 1.7.3
|
||||
@@ -3907,6 +4123,18 @@ snapshots:
|
||||
v-calendar: 3.1.2(@popperjs/core@2.11.8)(vue@3.5.17(typescript@5.8.3))
|
||||
vue: 3.5.17(typescript@5.8.3)
|
||||
vue-router: 4.5.1(vue@3.5.17(typescript@5.8.3))
|
||||
vue3-eslint: git+https://gitee.com/Cxx0822/vue3-eslint#849a4e0e783d3ecc57035fb2f92950998b6b2e41(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3))(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-config-standard@16.0.3(eslint-plugin-import@2.32.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0))(eslint-plugin-node@11.1.0(eslint@7.32.0))(eslint-plugin-promise@5.2.0(eslint@7.32.0))(eslint@7.32.0))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0))(eslint-plugin-node@11.1.0(eslint@7.32.0))(eslint-plugin-promise@5.2.0(eslint@7.32.0))(eslint-plugin-vue@8.7.1(eslint@7.32.0))(eslint@7.32.0)
|
||||
transitivePeerDependencies:
|
||||
- '@typescript-eslint/eslint-plugin'
|
||||
- '@typescript-eslint/parser'
|
||||
- '@vue/composition-api'
|
||||
- debug
|
||||
- eslint
|
||||
- eslint-config-standard
|
||||
- eslint-plugin-import
|
||||
- eslint-plugin-node
|
||||
- eslint-plugin-promise
|
||||
- eslint-plugin-vue
|
||||
|
||||
vue3-emoji-picker@1.1.8(typescript@5.8.3):
|
||||
dependencies:
|
||||
@@ -3916,6 +4144,17 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
|
||||
vue3-eslint@git+https://gitee.com/Cxx0822/vue3-eslint#849a4e0e783d3ecc57035fb2f92950998b6b2e41(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3))(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint-config-standard@16.0.3(eslint-plugin-import@2.32.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0))(eslint-plugin-node@11.1.0(eslint@7.32.0))(eslint-plugin-promise@5.2.0(eslint@7.32.0))(eslint@7.32.0))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0))(eslint-plugin-node@11.1.0(eslint@7.32.0))(eslint-plugin-promise@5.2.0(eslint@7.32.0))(eslint-plugin-vue@8.7.1(eslint@7.32.0))(eslint@7.32.0):
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)(typescript@5.8.3)
|
||||
'@typescript-eslint/parser': 5.62.0(eslint@7.32.0)(typescript@5.8.3)
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard: 16.0.3(eslint-plugin-import@2.32.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0))(eslint-plugin-node@11.1.0(eslint@7.32.0))(eslint-plugin-promise@5.2.0(eslint@7.32.0))(eslint@7.32.0)
|
||||
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.3))(eslint@7.32.0)
|
||||
eslint-plugin-node: 11.1.0(eslint@7.32.0)
|
||||
eslint-plugin-promise: 5.2.0(eslint@7.32.0)
|
||||
eslint-plugin-vue: 8.7.1(eslint@7.32.0)
|
||||
|
||||
vue@3.5.17(typescript@5.8.3):
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.17
|
||||
@@ -3926,10 +4165,6 @@ snapshots:
|
||||
optionalDependencies:
|
||||
typescript: 5.8.3
|
||||
|
||||
webidl-conversions@7.0.0: {}
|
||||
|
||||
whatwg-mimetype@3.0.0: {}
|
||||
|
||||
which-boxed-primitive@1.1.1:
|
||||
dependencies:
|
||||
is-bigint: 1.1.0
|
||||
|
||||
BIN
public/logo.ico
Normal file
BIN
public/logo.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
BIN
src/assets/logo.png
Normal file
BIN
src/assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 206 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 705 KiB |
13
src/components/ArriveButton.vue
Normal file
13
src/components/ArriveButton.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<span style="color: darkgrey; align-self: center;">
|
||||
已经到底咯~
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
111
src/components/DailyCard.vue
Normal file
111
src/components/DailyCard.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div class="daily-card record-card-item">
|
||||
<div class="card-item__icon">
|
||||
<slot name="icon"></slot>
|
||||
</div>
|
||||
|
||||
<div class="card-item__body">
|
||||
<span class="card-item__title">
|
||||
{{ props.title }}
|
||||
</span>
|
||||
<div class="card-item__content">
|
||||
<span>{{ props.content }}</span>
|
||||
<slot name="content"></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-item__extra">
|
||||
<slot name="extra"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
content: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.daily-card {
|
||||
padding: 0 5px;
|
||||
height: 60px;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 40px 1fr 100px;
|
||||
justify-items: center;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
|
||||
.card-item__icon {
|
||||
.iconfont {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.el-avatar {
|
||||
background: var(--el-color-primary);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.card-item__body {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
|
||||
justify-self: left;
|
||||
|
||||
.card-item__title {
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.card-item__content {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
|
||||
font-size: 0.9rem;
|
||||
color: #A9A9A9;
|
||||
}
|
||||
}
|
||||
|
||||
.card-item__extra {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
align-items: center;
|
||||
|
||||
justify-self: flex-end;
|
||||
}
|
||||
|
||||
.card-item__extra span:nth-child(1) {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.card-item__extra span:nth-child(2) {
|
||||
font-size: 0.9rem;
|
||||
color: #A9A9A9;
|
||||
}
|
||||
|
||||
hr {
|
||||
height: 1px;
|
||||
background-color: #cccccc;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.daily-card:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
35
src/components/DailySummary.vue
Normal file
35
src/components/DailySummary.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<div class="daily-summary">
|
||||
<span class="date">{{ props.date }}</span>
|
||||
<div class="daily-summary__extra">
|
||||
<slot name="extra"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
date: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.daily-summary {
|
||||
padding: 0 5px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: gray;
|
||||
font-size: 0.9rem;
|
||||
font-weight: bold;
|
||||
|
||||
.daily-summary__extra {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
101
src/components/DraggableButton.vue
Normal file
101
src/components/DraggableButton.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<el-button ref="buttonRef" type="primary" :icon="Plus"
|
||||
circle size="large" :style="style" style="font-size: 1.8rem;"
|
||||
@mousedown="startDrag" @touchstart="startDrag"/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, defineEmits, onMounted, reactive, ref } from 'vue'
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
|
||||
const buttonRef = ref()
|
||||
|
||||
const buttonPos = reactive({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0
|
||||
})
|
||||
|
||||
const startPos = reactive({
|
||||
x: 0,
|
||||
y: 0
|
||||
})
|
||||
|
||||
const offsetPos = reactive({
|
||||
x: 0,
|
||||
y: 0
|
||||
})
|
||||
|
||||
/**
|
||||
* 最小拖到阈值
|
||||
*/
|
||||
const dragThreshold = 5
|
||||
|
||||
const emit = defineEmits(['click'])
|
||||
|
||||
/**
|
||||
* 实时更新位置
|
||||
*/
|
||||
const style = computed(() => ({
|
||||
position: 'absolute',
|
||||
left: `${buttonPos.x}px`,
|
||||
top: `${buttonPos.y}px`,
|
||||
touchAction: 'none'
|
||||
}))
|
||||
|
||||
onMounted(() => {
|
||||
initButtonPosition()
|
||||
})
|
||||
|
||||
const initButtonPosition = () => {
|
||||
buttonPos.x = window.innerWidth - 50
|
||||
buttonPos.y = window.innerHeight - 120
|
||||
buttonPos.width = buttonRef.value.$el.offsetWidth
|
||||
buttonPos.height = buttonRef.value.$el.offsetHeight
|
||||
}
|
||||
|
||||
const startDrag = (event) => {
|
||||
event.preventDefault()
|
||||
|
||||
const touch = event.touches ? event.touches[0] : event
|
||||
|
||||
startPos.x = touch.clientX
|
||||
startPos.y = touch.clientY
|
||||
|
||||
offsetPos.x = touch.clientX - buttonPos.x
|
||||
offsetPos.y = touch.clientY - buttonPos.y
|
||||
|
||||
document.addEventListener('mousemove', onDrag)
|
||||
document.addEventListener('mouseup', stopDrag)
|
||||
document.addEventListener('touchmove', onDrag, { passive: false })
|
||||
document.addEventListener('touchend', stopDrag)
|
||||
}
|
||||
|
||||
const onDrag = (event) => {
|
||||
const touch = event.touches ? event.touches[0] : event
|
||||
|
||||
// 计算新位置,并限制在边界内
|
||||
const newX = touch.clientX - offsetPos.x
|
||||
const newY = touch.clientY - offsetPos.y
|
||||
|
||||
buttonPos.x = Math.max(0, Math.min(newX, window.innerWidth - buttonPos.width))
|
||||
buttonPos.y = Math.max(40, Math.min(newY, window.innerHeight - buttonPos.height - 70))
|
||||
}
|
||||
|
||||
const stopDrag = (event) => {
|
||||
document.removeEventListener('mousemove', onDrag)
|
||||
document.removeEventListener('mouseup', stopDrag)
|
||||
document.removeEventListener('touchmove', onDrag)
|
||||
document.removeEventListener('touchend', stopDrag)
|
||||
|
||||
const touch = event.changedTouches ? event.changedTouches[0] : event
|
||||
const deltaX = Math.abs(touch.clientX - startPos.x)
|
||||
const deltaY = Math.abs(touch.clientY - startPos.y)
|
||||
|
||||
// 此时为点击
|
||||
if (deltaX <= dragThreshold && deltaY <= dragThreshold) {
|
||||
emit('click', event)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
129
src/components/Food/FoodCard.vue
Normal file
129
src/components/Food/FoodCard.vue
Normal file
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<div class="life-record-card card-item">
|
||||
<el-carousel v-if="props.foodItem?.recordList.length > 0"
|
||||
arrow="always" @change="changeFoodEvent">
|
||||
<el-carousel-item v-for="(item, index) in getFoodRecord(props.foodItem?.recordList)"
|
||||
:key="index">
|
||||
<el-image :src="serviceFileRootPath + item.imageUrl"
|
||||
fit="contain" alt="暂无成果">
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<span>暂无图片</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
|
||||
<el-empty v-else description="暂无成果" />
|
||||
|
||||
<div class="record-info">
|
||||
<div class="top-container">
|
||||
<div class="info-list">
|
||||
<div class="info-item">
|
||||
<span class="title">{{ props.foodItem.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="button-info">
|
||||
<el-button type="primary" :icon="Edit" circle :size="'small'"
|
||||
@click="editFoodRecipeClick"/>
|
||||
<el-button type="primary" :icon="MoreFilled" circle :size="'small'"
|
||||
@click="viewFoodRecipeClick()" />
|
||||
<el-button type="danger" :icon="DeleteFilled" circle :size="'small'"
|
||||
@click="deleteFoodRecipeClick()" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bottom-container">
|
||||
<div class="info-list">
|
||||
<div class="info-item category-item">
|
||||
<i class="fa-solid fa-utensils"></i>
|
||||
{{ props.foodItem.category }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="props.foodItem?.recordList.length > 0" class="info-list">
|
||||
<div class="info-item">
|
||||
<i class="fa-solid fa-flag"></i>
|
||||
{{ props.foodItem.recordList[foodCardInfo.currentFoodIndex].person}}
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<i class="fa-regular fa-calendar-check"></i>
|
||||
{{ formatDate(props.foodItem.recordList[foodCardInfo.currentFoodIndex].date) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<span>暂无成果</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Edit, MoreFilled, DeleteFilled } from '@element-plus/icons-vue'
|
||||
import { useFoodStore } from '@/store/food'
|
||||
import { defineProps, onMounted, PropType, reactive } from 'vue'
|
||||
import { IFoodRecipe, IFoodRecord } from '@/types/food.ts'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { formatDate } from 'vue3-common/utils/dateUtil'
|
||||
import { deepCopyObject } from 'vue3-common/utils/dataUtil'
|
||||
import { commonElMessageBox } from 'vue3-common/utils/elUtil'
|
||||
import { serviceFileRootPath } from '@/utils'
|
||||
import { isMobile } from 'vue3-common/utils/layoutUtil'
|
||||
|
||||
const router = useRouter()
|
||||
const foodStore = useFoodStore()
|
||||
|
||||
const foodCardInfo = reactive({
|
||||
recommendRate: 0,
|
||||
currentFoodIndex: 0
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
foodItem: {
|
||||
required: true,
|
||||
type: Object as PropType<IFoodRecipe>
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
foodCardInfo.recommendRate = props.foodItem?.recommendRate as number
|
||||
})
|
||||
|
||||
const getFoodRecord = (recordList: IFoodRecord[]) => {
|
||||
return recordList.filter((item) => item.imageUrl)
|
||||
}
|
||||
|
||||
const editFoodRecipeClick = async () => {
|
||||
const id = props.foodItem?.id
|
||||
foodStore.foodRecipeApiType = 'UPDATE'
|
||||
await foodStore.queryFoodRecipe(id as number)
|
||||
// foodStore.foodRecipeDialog = {
|
||||
// title: '编辑菜谱',
|
||||
// visible: true,
|
||||
// data: deepCopyObject(foodStore.currentFoodRecipe)
|
||||
// }
|
||||
}
|
||||
|
||||
const viewFoodRecipeClick = async () => {
|
||||
await router.push({ name: 'MobileHomeFoodDetailId', params: { id: props.foodItem?.id } })
|
||||
}
|
||||
|
||||
const deleteFoodRecipeClick = () => {
|
||||
foodStore.foodRecipeApiType = 'DELETE'
|
||||
commonElMessageBox(`请确认是否删除该菜谱: ${props.foodItem?.name}?`).then(() => {
|
||||
foodStore.handleFoodRecipeApi(props.foodItem?.id as number)
|
||||
})
|
||||
}
|
||||
|
||||
const changeFoodEvent = (current: number) => {
|
||||
foodCardInfo.currentFoodIndex = current
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@use "@/styles/life.record.module.scss";
|
||||
</style>
|
||||
114
src/components/Food/FoodDaily.vue
Normal file
114
src/components/Food/FoodDaily.vue
Normal file
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div class="food-daily">
|
||||
<v-calendar ref="calendarRef" :attributes='dailyInfo.attrs'
|
||||
@dayclick="dateClick" @did-move="changeMonthClick"
|
||||
:is-dark="appStore.isDark"
|
||||
class="food-calendar card-item"/>
|
||||
|
||||
<food-daily-item :date="dailyInfo.calendarDate"/>
|
||||
|
||||
<draggable-button @click="addClick"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import FoodDailyItem from '@/components/Food/FoodDailyItem.vue'
|
||||
import DraggableButton from '@/components/DraggableButton.vue'
|
||||
import { reactive, watch, ref, onMounted } from 'vue'
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
import { formatDate } from 'vue3-common/utils/dateUtil'
|
||||
import { useAppStore } from '@/store/app'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const calendarRef = ref()
|
||||
const foodStore = useFoodStore()
|
||||
const appStore = useAppStore()
|
||||
const router = useRouter()
|
||||
|
||||
onMounted(() => {
|
||||
const { month } = foodStore.queryRecord
|
||||
calendarRef.value.move(new Date(month))
|
||||
})
|
||||
|
||||
watch(() => foodStore.isRefresh, () => {
|
||||
updateInfo()
|
||||
})
|
||||
|
||||
const dailyInfo = reactive({
|
||||
calendarDate: '',
|
||||
attrs: [] as any[]
|
||||
})
|
||||
|
||||
const addClick = () => {
|
||||
foodStore.foodRecordApiType = 'ADD'
|
||||
foodStore.currentFoodRecord = foodStore.getEmptyFoodRecord()
|
||||
router.push({ name: 'MobileHomeFoodRecordForm' })
|
||||
}
|
||||
|
||||
const updateInfo = () => {
|
||||
getCalendarMoveDate()
|
||||
getCalendarAttrDates()
|
||||
}
|
||||
|
||||
const getCalendarMoveDate = () => {
|
||||
dailyInfo.calendarDate = formatDate(new Date())
|
||||
|
||||
const foodLength = foodStore.foodRecordList.length
|
||||
if (foodLength > 0) {
|
||||
const today = formatDate(new Date())
|
||||
if (foodStore.foodRecordList.some((item) => item.date === today)) {
|
||||
dailyInfo.calendarDate = today
|
||||
} else {
|
||||
dailyInfo.calendarDate = formatDate(foodStore.foodRecordList[foodLength - 1].date)
|
||||
}
|
||||
} else {
|
||||
dailyInfo.calendarDate = ''
|
||||
}
|
||||
}
|
||||
|
||||
const getCalendarAttrDates = () => {
|
||||
dailyInfo.attrs = []
|
||||
dailyInfo.attrs.push({
|
||||
key: 'today',
|
||||
highlight: true,
|
||||
dates: new Date()
|
||||
})
|
||||
|
||||
const dateList = foodStore.foodRecordList.map((item) => item.date)
|
||||
dailyInfo.attrs.push({
|
||||
dot: 'red',
|
||||
dates: [...new Set(dateList)]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择日期事件
|
||||
* @param day 日期
|
||||
*/
|
||||
const dateClick = (day) => {
|
||||
dailyInfo.calendarDate = formatDate(day.date)
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择月份事件
|
||||
*/
|
||||
const changeMonthClick = async (value) => {
|
||||
foodStore.queryRecord.month = value[0].id
|
||||
await foodStore.refreshInfo()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.food-daily {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1vh;
|
||||
|
||||
.food-calendar {
|
||||
width: 100%;
|
||||
justify-self: center;
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
108
src/components/Food/FoodDailyItem.vue
Normal file
108
src/components/Food/FoodDailyItem.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<el-empty v-if="dailyItemInfo.itemList.length === 0" />
|
||||
|
||||
<div v-else class="food-daily-item card-item">
|
||||
<daily-summary :date="dailyItemInfo.date"/>
|
||||
|
||||
<div class="daily-list">
|
||||
<daily-card v-for="(item, index) in dailyItemInfo.itemList"
|
||||
:key="index" @click="selectFoodRecordClick(item)"
|
||||
:title="item.name" :content="item.category">
|
||||
<template #icon>
|
||||
<el-avatar>{{ item.category.charAt(0) }}</el-avatar>
|
||||
</template>
|
||||
|
||||
<template #extra>
|
||||
<span>{{ item.person }}</span>
|
||||
<span>{{ item.date }}</span>
|
||||
</template>
|
||||
</daily-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import DailySummary from '@/components/DailySummary.vue'
|
||||
import DailyCard from '@/components/DailyCard.vue'
|
||||
import { defineProps, onMounted, reactive, watch } from 'vue'
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useAppStore } from '@/store/app.ts'
|
||||
import { formatChineseDate } from 'vue3-common/utils/dateUtil'
|
||||
import { IFoodRecord } from '@/types/food.ts'
|
||||
|
||||
const foodStore = useFoodStore()
|
||||
const appStore = useAppStore()
|
||||
const router = useRouter()
|
||||
|
||||
const props = defineProps({
|
||||
date: {
|
||||
required: true,
|
||||
type: String
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => props.date, () => {
|
||||
getFoodDailyByDate(props.date as string)
|
||||
})
|
||||
|
||||
watch(() => foodStore.isRefresh, () => {
|
||||
getFoodDailyByDate(props.date as string)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getFoodDailyByDate(props.date as string)
|
||||
})
|
||||
|
||||
const dailyItemInfo = reactive({
|
||||
date: '',
|
||||
itemList: [] as IFoodRecord[]
|
||||
})
|
||||
|
||||
/**
|
||||
* 根据日期获取当天的美食记录
|
||||
* @param date
|
||||
*/
|
||||
const getFoodDailyByDate = (date: string) => {
|
||||
if (date) {
|
||||
dailyItemInfo.date = formatChineseDate(date, 'MM月DD日 dddd')
|
||||
dailyItemInfo.itemList = []
|
||||
|
||||
foodStore.foodRecordList.forEach((item) => {
|
||||
if (item.date === date) {
|
||||
dailyItemInfo.itemList.push(item)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
dailyItemInfo.itemList = []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击美食记录按钮
|
||||
* @param foodRecord 美食记录
|
||||
*/
|
||||
const selectFoodRecordClick = async (foodRecord: IFoodRecord) => {
|
||||
foodStore.foodRecordApiType = 'UPDATE'
|
||||
|
||||
appStore.isShowMobileBack = true
|
||||
foodStore.currentFoodRecord = foodRecord
|
||||
await router.push({ name: 'MobileHomeFoodRecordForm' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.food-daily-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1vh;
|
||||
padding: 1vh 0.5vw;
|
||||
|
||||
.daily-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1vh;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
97
src/components/Food/FoodRecipe.vue
Normal file
97
src/components/Food/FoodRecipe.vue
Normal file
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div ref="foodRecipeRef" class="food-recipe">
|
||||
<div class="query-item card-item">
|
||||
<span>分类:</span>
|
||||
<el-segmented v-model="foodStore.queryRecipe.category"
|
||||
:options="foodCategoryList"
|
||||
@change="queryFoodSegmented"/>
|
||||
</div>
|
||||
|
||||
<div class="food-list">
|
||||
<el-empty v-if="foodStore.foodRecipeList.length === 0" description="暂无美食记录"/>
|
||||
|
||||
<div v-else class="list">
|
||||
<food-card v-for="(item, index) in foodStore.foodRecipeList"
|
||||
:key="index" :food-item="item"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<arrive-bottom v-if="foodStore.isScrollEnd"/>
|
||||
|
||||
<draggable-button @click="addClick"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import FoodCard from '@/components/Food/FoodCard.vue'
|
||||
import ArriveBottom from '@/components/ArriveButton.vue'
|
||||
import DraggableButton from '@/components/DraggableButton.vue'
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const foodRecipeRef = ref()
|
||||
const foodStore = useFoodStore()
|
||||
const router = useRouter()
|
||||
|
||||
const foodCategoryList = [{
|
||||
label: '全部',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
label: '家常菜',
|
||||
value: '家常菜'
|
||||
}]
|
||||
|
||||
onMounted(async () => {
|
||||
foodRecipeRef.value.addEventListener('scroll', handleScroll)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
foodRecipeRef.value.removeEventListener('scroll', handleScroll)
|
||||
})
|
||||
|
||||
const addClick = () => {
|
||||
foodStore.foodRecordApiType = 'ADD'
|
||||
foodStore.currentFoodRecord = foodStore.getEmptyFoodRecord()
|
||||
router.push({ name: 'MobileHomeFoodRecordForm' })
|
||||
}
|
||||
|
||||
const queryFoodSegmented = async () => {
|
||||
foodStore.pageInfo.currentPage = 1
|
||||
foodStore.foodRecipeList = []
|
||||
await foodStore.queryFoodRecipeList()
|
||||
}
|
||||
|
||||
const handleScroll = () => {
|
||||
const el = foodRecipeRef.value
|
||||
if (el.scrollTop + el.clientHeight >= el.scrollHeight - 10) {
|
||||
const { currentPage, pageSize, totalSize } = foodStore.pageInfo
|
||||
foodStore.isScrollEnd = pageSize * currentPage > totalSize
|
||||
if (!foodStore.isScrollEnd) {
|
||||
foodStore.pageInfo.currentPage++
|
||||
foodStore.queryFoodRecipeList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.food-recipe {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
.query-item {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.food-list {
|
||||
.list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
75
src/components/Food/FoodTimeline.vue
Normal file
75
src/components/Food/FoodTimeline.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<div class="food-timeline">
|
||||
<el-date-picker
|
||||
v-model="currentYear"
|
||||
type="year" placeholder="请选择年份"
|
||||
style="width: 100px;"
|
||||
@change="changeDateEvent"
|
||||
/>
|
||||
|
||||
<el-timeline v-if="foodStore.foodRecordList.length > 0">
|
||||
<el-timeline-item v-for="(item, index) in foodStore.foodRecordList"
|
||||
:key="index" :timestamp="formatDate(item.date)" placement="top">
|
||||
<div class="timeline-info card-item">
|
||||
<span>{{ item.name }}</span>
|
||||
<el-image :src="item.imageUrl"
|
||||
fit="contain" alt="暂无成果">
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<span>暂无图片</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
|
||||
<el-empty v-else description="暂无日记"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { formatDate, getCurrentYear, getStartEndDate } from 'vue3-common/utils/dateUtil'
|
||||
import { serviceFileRootPath } from '@/utils'
|
||||
|
||||
const currentYear = ref(new Date())
|
||||
const foodStore = useFoodStore()
|
||||
|
||||
onMounted(async () => {
|
||||
const dateInfo = getStartEndDate(getCurrentYear(), 'year')
|
||||
foodStore.queryDateList[0] = dateInfo[0]
|
||||
foodStore.queryDateList[1] = dateInfo[1]
|
||||
await foodStore.refreshInfo()
|
||||
})
|
||||
|
||||
const changeDateEvent = async (value: Date) => {
|
||||
const dateInfo = getStartEndDate(value, 'year')
|
||||
foodStore.queryDateList[0] = dateInfo[0]
|
||||
foodStore.queryDateList[1] = dateInfo[1]
|
||||
await foodStore.refreshInfo()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.food-timeline {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.el-date-editor {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.el-timeline {
|
||||
padding: 10px;
|
||||
|
||||
.timeline-info {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
105
src/components/Moment/MomentCard.vue
Normal file
105
src/components/Moment/MomentCard.vue
Normal file
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div class="moment-card card-item">
|
||||
<el-avatar src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png" />
|
||||
|
||||
<div class="moment-content">
|
||||
<span class="username">Cxx0822</span>
|
||||
<p>今天烧了个红烧鱼 非常不错!</p>
|
||||
|
||||
<el-image style="width: 200px; height: 200px"
|
||||
src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg" fit="fill" />
|
||||
|
||||
<p>1小时前</p>
|
||||
|
||||
<div class="button-container">
|
||||
<el-button circle size="small">
|
||||
<i class="fa-regular fa-thumbs-up"></i>
|
||||
</el-button>
|
||||
<el-button circle size="small"
|
||||
@click="momentInfo.isShowComment = !momentInfo.isShowComment">
|
||||
<i class="fa-regular fa-comment"></i>
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div v-if="momentInfo.isShowComment" class="moment-comment">
|
||||
<el-input
|
||||
v-model="momentInfo.comment"
|
||||
maxlength="100" placeholder="平等表达 友善交流"
|
||||
show-word-limit type="textarea" :rows="3"
|
||||
@blur="momentInfo.isShowComment = false"
|
||||
/>
|
||||
|
||||
<el-popover ref="emojiPopoverRef" placement="bottom" :width="300" trigger="click">
|
||||
<template #default>
|
||||
<div class="emoji-panel">
|
||||
<EmojiPicker :native="true" @select="onSelectEmoji" />
|
||||
</div>
|
||||
</template>
|
||||
<template #reference>
|
||||
<el-button circle>
|
||||
<i class="fa-regular fa-face-smile"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
|
||||
<el-button type="primary"
|
||||
:disabled="momentInfo.comment.length === 0"
|
||||
@click="sendCommentClick"
|
||||
class="send-button">发送</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import EmojiPicker from 'vue3-emoji-picker'
|
||||
import 'vue3-emoji-picker/css'
|
||||
import { Edit } from '@element-plus/icons-vue'
|
||||
import { reactive } from 'vue'
|
||||
|
||||
const momentInfo = reactive({
|
||||
isShowComment: false,
|
||||
comment: ''
|
||||
})
|
||||
|
||||
const onSelectEmoji = (emoji) => {
|
||||
|
||||
}
|
||||
|
||||
const sendCommentClick = async () => {
|
||||
momentInfo.comment = ''
|
||||
momentInfo.isShowComment = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.moment-card {
|
||||
padding: 10px;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 50px 1fr;
|
||||
gap: 10px;
|
||||
|
||||
.moment-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-weight: bold;
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
.moment-comment {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 40px 60px;
|
||||
justify-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
172
src/components/Stats/FoodChart.vue
Normal file
172
src/components/Stats/FoodChart.vue
Normal file
@@ -0,0 +1,172 @@
|
||||
<template>
|
||||
<div class="food-chart"
|
||||
element-loading-text="正在加载中...">
|
||||
<div class="chart-container card-item">
|
||||
<div class="content">
|
||||
<span class="title">📊记录统计</span>
|
||||
</div>
|
||||
<div class="chart" ref="foodDailyChartRef"/>
|
||||
</div>
|
||||
<div class="chart-container card-item">
|
||||
<div class="content">
|
||||
<span class="title">📊类别统计</span>
|
||||
</div>
|
||||
<div class="chart" ref="foodCategoryChartRef"/>
|
||||
</div>
|
||||
<div class="chart-container card-item">
|
||||
<div class="content">
|
||||
<span class="title">📊排行榜</span>
|
||||
</div>
|
||||
<div class="chart rank-chart">
|
||||
<food-rank />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import FoodRank from '@/components/Stats/FoodRank.vue'
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import { lineChartOptions, barChartOptions, pieChartOptions } from 'vue3-common/utils/eChartsUtil'
|
||||
import { useFoodStore } from '@/store/food'
|
||||
import { useAppStore } from '@/store/app.ts'
|
||||
import type { IStatsChart } from 'vue3-common/types'
|
||||
import { isMobile } from 'vue3-common/utils/layoutUtil'
|
||||
|
||||
const foodStore = useFoodStore()
|
||||
const appStore = useAppStore()
|
||||
|
||||
const foodDailyChartRef = ref<HTMLElement>()
|
||||
const foodCategoryChartRef = ref<HTMLElement>()
|
||||
const billRankChartRef = ref<HTMLElement>()
|
||||
|
||||
let foodDailyChart: echarts.ECharts
|
||||
let foodCategoryChart: echarts.ECharts
|
||||
let billRankChart: echarts.ECharts
|
||||
|
||||
watch(() => foodStore.isRefresh, () => {
|
||||
updateChart()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
foodDailyChart = echarts.init(foodDailyChartRef.value as HTMLElement, appStore.isDark ? 'dark' : null)
|
||||
foodCategoryChart = echarts.init(foodCategoryChartRef.value as HTMLElement, appStore.isDark ? 'dark' : null)
|
||||
// billRankChart = echarts.init(billRankChartRef.value as HTMLElement, appStore.isDark ? 'dark' : null)
|
||||
})
|
||||
|
||||
const updateChart = () => {
|
||||
const MAX_CATEGORY_COUNT = 5
|
||||
const MAX_RANK_COUNT = 10
|
||||
|
||||
// 记录统计图
|
||||
// const dailyXAxis = foodStore.billDateStatsList.map((item) => item.name)
|
||||
// const dailyYAxis = foodStore.billDateStatsList.map((item) => item.value)
|
||||
const dailyXAxis = ['2024-01', '2024-02']
|
||||
const dailyYAxis = ['10', '6']
|
||||
foodDailyChart.setOption(lineChartOptions('日期', dailyXAxis, '次数(次)', dailyYAxis))
|
||||
//
|
||||
// // 账本统计图
|
||||
// const bookXAxis = foodStore.billBookStatsList.map((item) => item.name)
|
||||
// const bookYAxis = foodStore.billBookStatsList.map((item) => item.value)
|
||||
// billBookChart.setOption(barChartOptions('账本', bookXAxis, '金额(元)', bookYAxis))
|
||||
// billBookChart.setOption({
|
||||
// series: [{
|
||||
// itemStyle: {
|
||||
// color: foodStore.billType === 'expensive' ? '#CA6C65' : '#6FBB69'
|
||||
// },
|
||||
// markPoint: {
|
||||
// label: {
|
||||
// color: '#FFFFFF'
|
||||
// }
|
||||
// }
|
||||
// }]
|
||||
// })
|
||||
//
|
||||
// 类别统计图
|
||||
const foodCategoryList = [
|
||||
{ value: 12, name: '家常菜' },
|
||||
{ value: 10, name: '鲁菜' },
|
||||
{ value: 8, name: '川菜' },
|
||||
{ value: 5, name: '淮扬菜' },
|
||||
{ value: 2, name: '其他' }
|
||||
]
|
||||
foodCategoryChart.setOption(pieChartOptions('category', '次', foodCategoryList))
|
||||
//
|
||||
// // 排行榜统计图
|
||||
// const billRankList: IStatsChart[] = []
|
||||
// const resultList = foodStore.billRecordList.filter((item) => item.type === foodStore.billType)
|
||||
// resultList.sort((a, b) => b.amount - a.amount).forEach((item) => {
|
||||
// billRankList.push({
|
||||
// name: item.content,
|
||||
// value: item.amount
|
||||
// })
|
||||
// })
|
||||
//
|
||||
// if (!isMobile()) {
|
||||
// billRankChart.setOption(pieChartOptions('rank', '元', billRankList.slice(0, MAX_RANK_COUNT)))
|
||||
// }
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.food-chart {
|
||||
display: grid;
|
||||
grid-template-rows: repeat(3, 1fr);
|
||||
gap: 10px;
|
||||
|
||||
.chart-container {
|
||||
display: grid;
|
||||
grid-template-rows: 40px 1fr;
|
||||
|
||||
.content {
|
||||
padding: 0 5px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #009FA8;
|
||||
|
||||
.title {
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.chart {
|
||||
height: 300px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.rank-chart {
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
|
||||
.rank-item {
|
||||
padding: 10px;
|
||||
display: grid;
|
||||
grid-template-columns: 20px 1fr 80px;
|
||||
align-items: center;
|
||||
|
||||
.rank-amount {
|
||||
justify-self: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
.rank-item:nth-child(1) {
|
||||
color: #d4af37;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.rank-item:nth-child(2) {
|
||||
color: #a8a8a8;
|
||||
}
|
||||
|
||||
.rank-item:nth-child(3) {
|
||||
color: #b87333;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
33
src/components/Stats/FoodRank.vue
Normal file
33
src/components/Stats/FoodRank.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<div v-for="(item, index) in foodRankList.slice(0, MAX_RANK_COUNT)"
|
||||
:key="index" class="rank-item">
|
||||
<span class="rank-index">{{ index+1 }}.</span>
|
||||
<span class="rank-content">{{ item.value }}</span>
|
||||
<span class="rank-amount">{{ item.count }} 次</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useFoodStore } from '@/store/food'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const foodStore = useFoodStore()
|
||||
|
||||
const MAX_RANK_COUNT = 10
|
||||
|
||||
const foodRankList = computed(() => {
|
||||
return [{
|
||||
value: '红烧肉',
|
||||
count: 10
|
||||
}, {
|
||||
value: '红烧鱼',
|
||||
count: 8
|
||||
}, {
|
||||
value: '排骨汤',
|
||||
count: 5
|
||||
}, {
|
||||
value: '红烧带鱼',
|
||||
count: 2
|
||||
}]
|
||||
})
|
||||
</script>
|
||||
53
src/components/Stats/FoodStats.vue
Normal file
53
src/components/Stats/FoodStats.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div class="common-statistics">
|
||||
<stats-card
|
||||
title="菜谱总数" color="#CA6C65" unit="个" icon="iconfont icon-caipu"
|
||||
:value="foodStore.foodStats.recipeCount"/>
|
||||
|
||||
<stats-card
|
||||
title="菜谱类别" color="#6FBB69" unit="种" icon="iconfont icon-leibie"
|
||||
:value="foodStore.foodStats.categoryCount"/>
|
||||
|
||||
<stats-card
|
||||
title="做菜次数" color="#E4BF62" unit="次" icon="iconfont icon-cishu"
|
||||
:value="foodStore.foodStats.workCount"/>
|
||||
|
||||
<stats-card
|
||||
title="平均次数" color="#5470C6" unit="次/月" icon="iconfont icon-pinlv"
|
||||
:value="getAverageCount()"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import StatsCard from '@/components/StatsCard.vue'
|
||||
import { onMounted } from 'vue'
|
||||
import { useFoodStore } from '@/store/food'
|
||||
|
||||
const foodStore = useFoodStore()
|
||||
|
||||
onMounted(async () => {
|
||||
await foodStore.queryFoodStats()
|
||||
})
|
||||
|
||||
const getAverageCount = () => {
|
||||
if (foodStore.queryDateType === 'month') {
|
||||
return foodStore.foodRecordList.length
|
||||
} else {
|
||||
const result = {}
|
||||
|
||||
// 遍历数据,按月统计出现次数
|
||||
foodStore.foodRecordList.forEach((item) => {
|
||||
const date = new Date(item.date)
|
||||
const month = String(date.getMonth() + 1)
|
||||
|
||||
if (result[month]) {
|
||||
result[month].count += 1
|
||||
} else {
|
||||
result[month] = { count: 1 }
|
||||
}
|
||||
})
|
||||
|
||||
return foodStore.foodRecordList.length / Object.keys(result).length
|
||||
}
|
||||
}
|
||||
</script>
|
||||
92
src/components/StatsCard.vue
Normal file
92
src/components/StatsCard.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div class="statistics-card card-item" :style="{color: props.color}">
|
||||
<span class="icon">
|
||||
<i :class="props.icon"></i>
|
||||
</span>
|
||||
|
||||
<div class="item">
|
||||
<span class="title">
|
||||
{{ props.title }}
|
||||
</span>
|
||||
|
||||
<div class="value">
|
||||
<span class="amount">
|
||||
{{ props.value }}
|
||||
</span>
|
||||
<span class="unit">
|
||||
{{ props.unit }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
icon: {
|
||||
required: true,
|
||||
type: String
|
||||
},
|
||||
title: {
|
||||
required: true,
|
||||
type: String
|
||||
},
|
||||
value: {
|
||||
required: true,
|
||||
type: [String, Number]
|
||||
},
|
||||
unit: {
|
||||
required: true,
|
||||
type: String
|
||||
},
|
||||
color: {
|
||||
required: true,
|
||||
type: String
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.statistics-card {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
display: grid;
|
||||
grid-template-columns: 30% 1fr;
|
||||
justify-items: center;
|
||||
align-items: center;
|
||||
|
||||
.iconfont, .icon {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 1vh;
|
||||
|
||||
.title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.value {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 0.5vw;
|
||||
|
||||
.amount {
|
||||
font-weight: bold;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.unit {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,14 +1,24 @@
|
||||
<template>
|
||||
<div class="tab-bar">
|
||||
<router-link to="/app/record" @click="routerClick()" class="item">
|
||||
<i class="fa-solid fa-house"></i>
|
||||
<router-link to="/record" @click="routerClick()" class="item">
|
||||
<i class="iconfont icon-jilu"></i>
|
||||
<span>记录</span>
|
||||
</router-link>
|
||||
|
||||
<router-link to="/app/stats" @click="routerClick()" class="item">
|
||||
<i class="fa-solid fa-house"></i>
|
||||
<router-link to="/stats" @click="routerClick()" class="item">
|
||||
<i class="iconfont icon-tongji"></i>
|
||||
<span>统计</span>
|
||||
</router-link>
|
||||
|
||||
<router-link to="/moment" @click="routerClick()" class="item">
|
||||
<i class="iconfont icon-pengyouquan"></i>
|
||||
<span>朋友圈</span>
|
||||
</router-link>
|
||||
|
||||
<router-link to="/profile" @click="routerClick()" class="item">
|
||||
<i class="fa-solid fa-user"></i>
|
||||
<span>个人中心</span>
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
circle color="#FFFFFF"
|
||||
@click="goBackClick" />
|
||||
<div v-else class="logo-container">
|
||||
<img :src="getAssetsImageFile('logo/logo.png')" alt="暂无图片" class="logo"/>
|
||||
<img :src="getAssetsImageFile('logo.png')" alt="暂无图片" class="logo"/>
|
||||
<span>{{ appStore.appName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,6 +12,10 @@ import 'element-plus/theme-chalk/dark/css-vars.css'
|
||||
import 'dayjs/locale/zh-cn'
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||
|
||||
// 引入日历组件
|
||||
import VCalendar from 'v-calendar'
|
||||
import 'v-calendar/style.css'
|
||||
|
||||
// 引入font-awesome
|
||||
// import 'font-awesome/css/font-awesome.min.css'
|
||||
import '@fortawesome/fontawesome-free/css/all.css'
|
||||
@@ -24,5 +28,6 @@ app.use(router)
|
||||
app.use(createPinia())
|
||||
// 使用Element UI Plus
|
||||
app.use(ElementPlus, { locale: zhCn })
|
||||
app.use(VCalendar, {})
|
||||
// 挂载到根组件上
|
||||
app.mount('#app')
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import Layout from '@/layout/index.vue'
|
||||
import { getRoutersByModules } from 'vue3-common/utils/routerUtil'
|
||||
import { routerMeta } from '@/views/meta.ts'
|
||||
|
||||
const routes = getRoutersByModules(import.meta.glob('@/views/app/**/*.vue'), Layout, routerMeta)
|
||||
|
||||
export default routes
|
||||
14
src/router/menu.ts
Normal file
14
src/router/menu.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { getRoutersByModules, sortRoutesByOrder } from 'vue3-common/utils/routerUtil'
|
||||
import Layout from '@/layout/index.vue'
|
||||
import { menuMeta } from '@/views/meta'
|
||||
|
||||
const modules = {
|
||||
...import.meta.glob('@/views/record/**/*.vue'),
|
||||
...import.meta.glob('@/views/stats/**/*.vue'),
|
||||
...import.meta.glob('@/views/moment/**/*.vue'),
|
||||
...import.meta.glob('@/views/profile/**/*.vue')
|
||||
}
|
||||
|
||||
const menuRoutes = getRoutersByModules(modules, Layout, menuMeta)
|
||||
|
||||
export default sortRoutesByOrder(menuRoutes)
|
||||
235
src/store/food.ts
Normal file
235
src/store/food.ts
Normal file
@@ -0,0 +1,235 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import type {
|
||||
IFoodRecipe, IFoodMaterial, IFoodStep,
|
||||
IFoodStats, IFoodRecord
|
||||
} from '@/types/food'
|
||||
// import {
|
||||
// addFoodRecipeApi, addFoodRecordApi, deleteFoodRecipeApi, deleteFoodRecordApi,
|
||||
// queryFoodNameListApi, queryFoodRecipeByIdApi, queryFoodRecipeApi, queryFoodRecordApi,
|
||||
// queryFoodStatsApi, updateFoodRecipeApi, updateFoodRecordApi, queryFoodCategoryApi
|
||||
// } from '@/apis/food.ts'
|
||||
// import { IDateType, ImagePathEnum } from '@/types'
|
||||
import type { IDialog, IHandleApi } from 'vue3-common/types'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { getCurrentMonth, getCurrentYear, getStartEndDate } from 'vue3-common/utils/dateUtil'
|
||||
import { isMobile } from 'vue3-common/utils/layoutUtil'
|
||||
|
||||
export const useFoodStore = defineStore('food', {
|
||||
state: () => ({
|
||||
foodRecipeList: [] as IFoodRecipe[],
|
||||
currentFoodRecipe: {
|
||||
id: 0,
|
||||
name: '',
|
||||
category: '',
|
||||
recommendRate: 0,
|
||||
remark: '',
|
||||
materialList: [],
|
||||
stepList: [],
|
||||
recordList: []
|
||||
} as IFoodRecipe,
|
||||
foodRecordList: [] as IFoodRecord[],
|
||||
currentFoodRecord: {
|
||||
id: 0,
|
||||
name: '',
|
||||
category: '',
|
||||
date: '',
|
||||
person: '',
|
||||
imageUrl: ''
|
||||
} as IFoodRecord,
|
||||
foodNameList: [] as string[],
|
||||
queryRecipe: {
|
||||
category: ''
|
||||
},
|
||||
queryRecord: {
|
||||
year: getCurrentYear(),
|
||||
month: getCurrentMonth()
|
||||
},
|
||||
queryDateType: 'month',
|
||||
queryDateList: [] as string[],
|
||||
foodRecipeApiType: 'ADD' as IHandleApi,
|
||||
foodRecipeDialog: {
|
||||
visible: false,
|
||||
title: '',
|
||||
data: {}
|
||||
} as IDialog<IFoodRecipe>,
|
||||
foodMaterialApiType: 'ADD' as IHandleApi,
|
||||
foodMaterialDialog: {
|
||||
visible: false,
|
||||
title: '',
|
||||
data: {}
|
||||
} as IDialog<IFoodMaterial>,
|
||||
editFoodMaterialIndex: 0,
|
||||
foodStepApiType: 'ADD' as IHandleApi,
|
||||
foodStepDialog: {
|
||||
visible: false,
|
||||
title: '',
|
||||
data: {}
|
||||
} as IDialog<IFoodStep>,
|
||||
editFoodStepIndex: 0,
|
||||
foodRecordApiType: 'ADD' as IHandleApi,
|
||||
foodRecordDialog: {
|
||||
visible: false,
|
||||
title: '',
|
||||
data: {}
|
||||
} as IDialog<IFoodRecord>,
|
||||
editFoodRecordIndex: 0,
|
||||
foodImagePath: 'food',
|
||||
rateColorList: ['#E2DEDC', '#F7BA2A', '#009FA8'],
|
||||
pageInfo: {
|
||||
currentPage: 1,
|
||||
pageSize: 3,
|
||||
totalSize: 1
|
||||
},
|
||||
isScrollEnd: false,
|
||||
foodCategoryList: [] as string[],
|
||||
foodStats: {
|
||||
recipeCount: 0,
|
||||
categoryCount: 0,
|
||||
workCount: 0
|
||||
} as IFoodStats,
|
||||
isRefresh: false
|
||||
}),
|
||||
actions: {
|
||||
async queryFoodRecipeList() {
|
||||
const { currentPage, pageSize } = this.pageInfo
|
||||
// const result = await queryFoodRecipeApi(currentPage, pageSize, this.queryRecipe)
|
||||
const foodRecord: IFoodRecipe = {
|
||||
category: '家常菜',
|
||||
materialList: [],
|
||||
name: '红烧鱼',
|
||||
recommendRate: 4,
|
||||
recordList: [],
|
||||
remark: '',
|
||||
stepList: []
|
||||
}
|
||||
this.foodRecipeList = []
|
||||
this.foodRecipeList.push(foodRecord)
|
||||
this.foodRecipeList.push(foodRecord)
|
||||
// this.foodRecipeList.push(...result.records)
|
||||
// this.pageInfo.totalSize = result.total
|
||||
},
|
||||
async queryFoodRecordList() {
|
||||
const { year, month } = this.queryRecord
|
||||
const date = this.queryDateType === 'year' ? year : month
|
||||
this.queryDateList = getStartEndDate(date, this.queryDateType)
|
||||
|
||||
const foodRecord: IFoodRecord = {
|
||||
category: '家常菜',
|
||||
date: '2025-06-23',
|
||||
id: 0,
|
||||
imageUrl: 'https://ts2.tc.mm.bing.net/th/id/OSK.277ab994364a7e09543c78379bab1d05?w=612&h=408&c=7&rs=1&qlt=80&o=6&cdv=1&cb=ircwebp1&pid=16.1',
|
||||
name: '红烧鱼',
|
||||
person: '哥哥'
|
||||
}
|
||||
const foodRecord2: IFoodRecord = {
|
||||
category: '家常菜',
|
||||
date: '2025-06-22',
|
||||
id: 0,
|
||||
imageUrl: 'https://ts2.tc.mm.bing.net/th/id/OSK.277ab994364a7e09543c78379bab1d05?w=612&h=408&c=7&rs=1&qlt=80&o=6&cdv=1&cb=ircwebp1&pid=16.1',
|
||||
name: '红烧鱼',
|
||||
person: '哥哥'
|
||||
}
|
||||
this.foodRecordList = []
|
||||
this.foodRecordList.push(foodRecord)
|
||||
this.foodRecordList.push(foodRecord2)
|
||||
// this.foodRecordList = await queryFoodRecordApi(this.queryDateList[0], this.queryDateList[1])
|
||||
},
|
||||
async queryFoodNameList() {
|
||||
// this.foodNameList = await queryFoodNameListApi()
|
||||
},
|
||||
async queryFoodRecipe(id: number) {
|
||||
// this.currentFoodRecipe = await queryFoodRecipeByIdApi(id)
|
||||
},
|
||||
async queryFoodStats() {
|
||||
// this.foodStats = await queryFoodStatsApi()
|
||||
this.isRefresh = !this.isRefresh
|
||||
},
|
||||
async queryFoodCategory() {
|
||||
// this.foodCategoryList = await queryFoodCategoryApi()
|
||||
},
|
||||
async handleFoodRecipeApi(id?: number) {
|
||||
const { data } = this.foodRecipeDialog
|
||||
switch (this.foodRecipeApiType) {
|
||||
case 'ADD':
|
||||
// await addFoodRecipeApi(data)
|
||||
ElMessage.success('新增菜谱成功')
|
||||
break
|
||||
case 'UPDATE':
|
||||
// await updateFoodRecipeApi(data.id as number, data)
|
||||
ElMessage.success('更新菜谱成功')
|
||||
break
|
||||
case 'DELETE':
|
||||
// await deleteFoodRecipeApi(id as number)
|
||||
ElMessage.success('删除菜谱成功')
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
this.foodRecipeDialog.visible = false
|
||||
await this.refreshInfo()
|
||||
},
|
||||
async handleFoodRecordApi(foodRecord: IFoodRecord) {
|
||||
switch (this.foodRecordApiType) {
|
||||
case 'ADD':
|
||||
// await addFoodRecordApi(foodRecord)
|
||||
ElMessage.success('新增成果成功')
|
||||
break
|
||||
case 'UPDATE':
|
||||
// await updateFoodRecordApi(foodRecord.id as number, foodRecord)
|
||||
ElMessage.success('更新成果成功')
|
||||
break
|
||||
case 'DELETE':
|
||||
// await deleteFoodRecordApi(foodRecord.id as number)
|
||||
ElMessage.success('删除成果成功')
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
this.foodRecordDialog.visible = false
|
||||
|
||||
if (!isMobile()) {
|
||||
await this.refreshInfo()
|
||||
}
|
||||
},
|
||||
async refreshInfo() {
|
||||
await this.queryFoodRecipeList()
|
||||
await this.queryFoodRecordList()
|
||||
this.isRefresh = !this.isRefresh
|
||||
},
|
||||
getEmptyFoodRecipe(): IFoodRecipe {
|
||||
return {
|
||||
category: '',
|
||||
materialList: [],
|
||||
name: '',
|
||||
recommendRate: 0,
|
||||
remark: '',
|
||||
stepList: [],
|
||||
recordList: []
|
||||
}
|
||||
},
|
||||
getEmptyFoodMaterial(): IFoodMaterial {
|
||||
return {
|
||||
amount: '',
|
||||
name: '',
|
||||
type: ''
|
||||
}
|
||||
},
|
||||
getEmptyFoodStep(): IFoodStep {
|
||||
return {
|
||||
content: '',
|
||||
imageUrl: '',
|
||||
sort: 0
|
||||
}
|
||||
},
|
||||
getEmptyFoodRecord(): IFoodRecord {
|
||||
return {
|
||||
category: '',
|
||||
person: '',
|
||||
date: '',
|
||||
id: 0,
|
||||
imageUrl: '',
|
||||
name: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -82,4 +82,4 @@ span, input {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@import "//at.alicdn.com/t/c/font_4793264_befv3sf3zdg.css";
|
||||
@import "//at.alicdn.com/t/c/font_4956432_suiec1b32.css";
|
||||
|
||||
90
src/styles/life.record.module.scss
Normal file
90
src/styles/life.record.module.scss
Normal file
@@ -0,0 +1,90 @@
|
||||
$card-image-height: 200px;
|
||||
|
||||
.life-record-card {
|
||||
display: grid;
|
||||
grid-template-rows: minmax($card-image-height, 26vh) 1fr;
|
||||
|
||||
.el-carousel {
|
||||
border-top-left-radius: 15px;
|
||||
border-top-right-radius: 15px;
|
||||
|
||||
.el-carousel__container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.el-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.record-info {
|
||||
border-top: 1px solid var(--el-color-primary);
|
||||
padding: 1vh;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.top-container, .bottom-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.info-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.el-rate {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
|
||||
.title {
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.category-item {
|
||||
color: #0d9488;
|
||||
}
|
||||
|
||||
.location-item {
|
||||
color: #0d9488;
|
||||
}
|
||||
|
||||
.amount-item {
|
||||
font-size: 1.2rem;
|
||||
color: #CFB53B;
|
||||
}
|
||||
|
||||
.fa-solid.fa-location-dot {
|
||||
color: #008000;
|
||||
}
|
||||
|
||||
.fa-solid.fa-flag {
|
||||
color: #0000FF;
|
||||
}
|
||||
|
||||
.fa-regular.fa-calendar-check {
|
||||
color: #FF0000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-container {
|
||||
font-size: small;
|
||||
color: #9C9C9C;
|
||||
}
|
||||
}
|
||||
}
|
||||
79
src/types/food.ts
Normal file
79
src/types/food.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
export type IFoodSegment = 'recipe' | 'daily' | 'timeline'
|
||||
|
||||
/**
|
||||
* 菜谱信息
|
||||
*/
|
||||
export interface IFoodRecipe {
|
||||
id?: number;
|
||||
/**
|
||||
* 美食名称
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* 美食菜系
|
||||
*/
|
||||
category: string;
|
||||
/**
|
||||
* 推荐指数
|
||||
*/
|
||||
recommendRate: number;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark: string;
|
||||
/**
|
||||
* 食材
|
||||
*/
|
||||
materialList: IFoodMaterial[];
|
||||
/**
|
||||
* 步骤
|
||||
*/
|
||||
stepList: IFoodStep[];
|
||||
/**
|
||||
* 成果
|
||||
*/
|
||||
recordList: IFoodRecord[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 食材信息
|
||||
*/
|
||||
export interface IFoodMaterial {
|
||||
type: string;
|
||||
name: string;
|
||||
amount: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 步骤信息
|
||||
*/
|
||||
export interface IFoodStep {
|
||||
sort: number;
|
||||
content: string;
|
||||
imageUrl: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 成果信息
|
||||
*/
|
||||
export interface IFoodRecord {
|
||||
id: number;
|
||||
name: string;
|
||||
category: string;
|
||||
date: string;
|
||||
person: string;
|
||||
imageUrl: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计信息
|
||||
*/
|
||||
export interface IFoodStats {
|
||||
recipeCount: number;
|
||||
categoryCount: number;
|
||||
workCount: number;
|
||||
}
|
||||
|
||||
export interface IFoodQuery {
|
||||
category: string;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<template>
|
||||
<div class="common-mobile-view">
|
||||
记录
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
@@ -1,11 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
登录
|
||||
<el-button type="primary" @click="loginClick">登录</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const loginClick = () => {
|
||||
router.push('/record')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { IRouteMetaConfig } from 'vue3-common/types'
|
||||
|
||||
export const routerMeta: IRouteMetaConfig = {
|
||||
export const menuMeta: IRouteMetaConfig = {
|
||||
'/record': {
|
||||
path: '/record',
|
||||
redirect: '/record/index'
|
||||
@@ -8,5 +8,13 @@ export const routerMeta: IRouteMetaConfig = {
|
||||
'/stats': {
|
||||
path: '/stats',
|
||||
redirect: '/stats/index'
|
||||
},
|
||||
'/moment': {
|
||||
path: '/moment',
|
||||
redirect: '/moment/index'
|
||||
},
|
||||
'/profile': {
|
||||
path: '/profile',
|
||||
redirect: '/profile/index'
|
||||
}
|
||||
}
|
||||
|
||||
17
src/views/moment/index.vue
Normal file
17
src/views/moment/index.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div class="common-mobile-view food-moment">
|
||||
<moment-card v-for="(item, index) in 4" :key="index"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import MomentCard from '@/components/Moment/MomentCard.vue'
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.food-moment {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,13 +1,12 @@
|
||||
<template>
|
||||
<div class="common-mobile-view">
|
||||
统计
|
||||
<div class="common-mobile-view profile">
|
||||
个人中心
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
85
src/views/record/index.vue
Normal file
85
src/views/record/index.vue
Normal file
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<div class="common-mobile-view food-record">
|
||||
<div class="food-query card-item">
|
||||
<el-input
|
||||
v-model="foodInfo.query"
|
||||
placeholder="请输入菜谱名称" :prefix-icon="Search" clearable
|
||||
:maxlength="20" show-word-limit
|
||||
/>
|
||||
|
||||
<div class="query-item">
|
||||
<span>查看类型:</span>
|
||||
<el-segmented v-model="foodInfo.recordSegment"
|
||||
:options="recordSegmentList"
|
||||
@change="changeRecordSegmentEvent()"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr style="width: 100%;"/>
|
||||
|
||||
<div v-if="foodInfo.recordSegment === 'recipe'" class="food-calendar">
|
||||
<food-recipe />
|
||||
</div>
|
||||
|
||||
<div v-if="foodInfo.recordSegment === 'daily'" class="food-calendar">
|
||||
<food-daily />
|
||||
</div>
|
||||
|
||||
<div v-if="foodInfo.recordSegment === 'timeline'" class="food-calendar">
|
||||
<food-timeline />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import FoodRecipe from '@/components/Food/FoodRecipe.vue'
|
||||
import FoodDaily from '@/components/Food/FoodDaily.vue'
|
||||
import FoodTimeline from '@/components/Food/FoodTimeline.vue'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { onMounted, reactive } from 'vue'
|
||||
import { useFoodStore } from '@/store/food'
|
||||
import { IFoodSegment } from '@/types/food'
|
||||
|
||||
const foodStore = useFoodStore()
|
||||
|
||||
const foodInfo = reactive({
|
||||
recordSegment: 'recipe' as IFoodSegment,
|
||||
query: ''
|
||||
})
|
||||
|
||||
const recordSegmentList = [{
|
||||
label: '菜谱',
|
||||
value: 'recipe'
|
||||
},
|
||||
{
|
||||
label: '日历',
|
||||
value: 'daily'
|
||||
},
|
||||
{
|
||||
label: '时间轴',
|
||||
value: 'timeline'
|
||||
}]
|
||||
|
||||
onMounted(() => {
|
||||
foodStore.refreshInfo()
|
||||
})
|
||||
|
||||
const changeRecordSegmentEvent = () => {
|
||||
foodStore.refreshInfo()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.food-record {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
|
||||
.food-query {
|
||||
padding: 5px 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
20
src/views/stats/index.vue
Normal file
20
src/views/stats/index.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<div class="common-mobile-view food-stats">
|
||||
<food-stats />
|
||||
|
||||
<food-chart />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import FoodStats from '@/components/Stats/FoodStats.vue'
|
||||
import FoodChart from '@/components/Stats/FoodChart.vue'
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.food-stats {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -11,5 +11,8 @@ export default defineConfig({
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src')
|
||||
}
|
||||
},
|
||||
server: {
|
||||
port: 5175
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user