@ -0,0 +1,13 @@
|
||||
# editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
@ -0,0 +1,42 @@
|
||||
/* eslint-env node */
|
||||
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended',
|
||||
'@vue/eslint-config-typescript',
|
||||
'@vue/eslint-config-prettier',
|
||||
],
|
||||
// 小程序全局变量
|
||||
globals: {
|
||||
uni: true,
|
||||
wx: true,
|
||||
WechatMiniprogram: true,
|
||||
getCurrentPages: true,
|
||||
UniApp: true,
|
||||
UniHelper: true,
|
||||
Page: true,
|
||||
AnyObject: true,
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
},
|
||||
rules: {
|
||||
'prettier/prettier': [
|
||||
'warn',
|
||||
{
|
||||
singleQuote: true,
|
||||
semi: false,
|
||||
printWidth: 100,
|
||||
trailingComma: 'all',
|
||||
endOfLine: 'auto',
|
||||
},
|
||||
],
|
||||
'vue/multi-word-component-names': ['off'],
|
||||
'vue/no-setup-props-destructure': ['off'],
|
||||
'vue/no-deprecated-html-element-is': ['off'],
|
||||
'@typescript-eslint/no-unused-vars': ['off'],
|
||||
},
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
.DS_Store
|
||||
*.local
|
@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npm run lint-staged
|
@ -0,0 +1,7 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"semi": false,
|
||||
"printWidth": 100,
|
||||
"trailingComma": "all",
|
||||
"endOfLine": "auto"
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
{
|
||||
// 推荐的扩展插件
|
||||
"recommendations": [
|
||||
"mrmaoddxxaa.create-uniapp-view", // 创建 uni-app 页面
|
||||
"uni-helper.uni-helper-vscode", // uni-app 代码提示
|
||||
"evils.uniapp-vscode", // uni-app 文档
|
||||
"vue.volar", // vue3 语法支持
|
||||
"vue.vscode-typescript-vue-plugin", // vue3 ts 插件
|
||||
"editorconfig.editorconfig", // editorconfig
|
||||
"dbaeumer.vscode-eslint", // eslint
|
||||
"esbenp.prettier-vscode" // prettier
|
||||
]
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
{
|
||||
// 在保存时格式化文件
|
||||
"editor.formatOnSave": true,
|
||||
// 文件格式化配置
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
// 配置语言的文件关联
|
||||
"files.associations": {
|
||||
"pages.json": "jsonc", // pages.json 可以写注释
|
||||
"manifest.json": "jsonc" // manifest.json 可以写注释
|
||||
}
|
||||
}
|
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script>
|
||||
var coverSupport =
|
||||
'CSS' in window &&
|
||||
typeof CSS.supports === 'function' &&
|
||||
(CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
|
||||
document.write(
|
||||
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||
(coverSupport ? ', viewport-fit=cover' : '') +
|
||||
'" />',
|
||||
)
|
||||
</script>
|
||||
<title></title>
|
||||
<!--preload-links-->
|
||||
<!--app-context-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<!--app-html-->
|
||||
</div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
// <script>
|
||||
// var _hmt = _hmt || []
|
||||
// ;(function () {
|
||||
// var hm = document.createElement('script')
|
||||
// hm.src = 'https://hm.baidu.com/hm.js?47837f06b6f8cf8ba80165ca9a80711'
|
||||
// var s = document.getElementsByTagName('script')[0]
|
||||
// s.parentNode.insertBefore(hm, s)
|
||||
// })()
|
||||
// </script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,94 @@
|
||||
{
|
||||
"name": "uni-app-chanko",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"dev:app": "uni -p app",
|
||||
"dev:app-android": "uni -p app-android",
|
||||
"dev:app-ios": "uni -p app-ios",
|
||||
"dev:custom": "uni -p",
|
||||
"dev:h5": "uni",
|
||||
"dev:h5:ssr": "uni --ssr",
|
||||
"dev:mp-alipay": "uni -p mp-alipay",
|
||||
"dev:mp-baidu": "uni -p mp-baidu",
|
||||
"dev:mp-kuaishou": "uni -p mp-kuaishou",
|
||||
"dev:mp-lark": "uni -p mp-lark",
|
||||
"dev:mp-qq": "uni -p mp-qq",
|
||||
"dev:mp-toutiao": "uni -p mp-toutiao",
|
||||
"dev:mp-weixin": "uni -p mp-weixin",
|
||||
"dev:quickapp-webview": "uni -p quickapp-webview",
|
||||
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
|
||||
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
|
||||
"build:app": "uni build -p app",
|
||||
"build:app-android": "uni build -p app-android",
|
||||
"build:app-ios": "uni build -p app-ios",
|
||||
"build:custom": "uni build -p",
|
||||
"build:h5": "uni build",
|
||||
"build:h5:ssr": "uni build --ssr",
|
||||
"build:mp-alipay": "uni build -p mp-alipay",
|
||||
"build:mp-baidu": "uni build -p mp-baidu",
|
||||
"build:mp-kuaishou": "uni build -p mp-kuaishou",
|
||||
"build:mp-lark": "uni build -p mp-lark",
|
||||
"build:mp-qq": "uni build -p mp-qq",
|
||||
"build:mp-toutiao": "uni build -p mp-toutiao",
|
||||
"build:mp-weixin": "uni build -p mp-weixin",
|
||||
"build:quickapp-webview": "uni build -p quickapp-webview",
|
||||
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
|
||||
"build:quickapp-webview-union": "uni build -p quickapp-webview-union",
|
||||
"tsc": "vue-tsc --noEmit --skipLibCheck",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||
"prepare": "husky install",
|
||||
"lint-staged": "lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,ts,vue}": [
|
||||
"eslint --fix"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@dcloudio/uni-app": "3.0.0-3090620231104002",
|
||||
"@dcloudio/uni-app-plus": "3.0.0-3090620231104002",
|
||||
"@dcloudio/uni-components": "3.0.0-3090620231104002",
|
||||
"@dcloudio/uni-h5": "3.0.0-3090620231104002",
|
||||
"@dcloudio/uni-mp-alipay": "3.0.0-3090620231104002",
|
||||
"@dcloudio/uni-mp-baidu": "3.0.0-3090620231104002",
|
||||
"@dcloudio/uni-mp-jd": "3.0.0-3090620231104002",
|
||||
"@dcloudio/uni-mp-kuaishou": "3.0.0-3090620231104002",
|
||||
"@dcloudio/uni-mp-lark": "3.0.0-3090620231104002",
|
||||
"@dcloudio/uni-mp-qq": "3.0.0-3090620231104002",
|
||||
"@dcloudio/uni-mp-toutiao": "3.0.0-3090620231104002",
|
||||
"@dcloudio/uni-mp-weixin": "3.0.0-3090620231104002",
|
||||
"@dcloudio/uni-mp-xhs": "3.0.0-3090620231104002",
|
||||
"@dcloudio/uni-quickapp-webview": "3.0.0-3090620231104002",
|
||||
"@dcloudio/uni-ui": "^1.4.28",
|
||||
"pinia": "2.0.27",
|
||||
"pinia-plugin-persistedstate": "^3.2.0",
|
||||
"vue": "^3.3.4",
|
||||
"vue-i18n": "^9.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dcloudio/types": "^3.4.3",
|
||||
"@dcloudio/uni-automator": "3.0.0-3090620231104002",
|
||||
"@dcloudio/uni-cli-shared": "3.0.0-3090620231104002",
|
||||
"@dcloudio/uni-stacktracey": "3.0.0-3090620231104002",
|
||||
"@dcloudio/uni-vue-devtools": "3.0.0-alpha-3080220230511001",
|
||||
"@dcloudio/vite-plugin-uni": "3.0.0-3090620231104002",
|
||||
"@rushstack/eslint-patch": "^1.1.4",
|
||||
"@types/node": "^18.11.9",
|
||||
"@uni-helper/uni-app-types": "^0.5.12",
|
||||
"@uni-helper/uni-ui-types": "^0.5.11",
|
||||
"@vue/eslint-config-prettier": "^7.0.0",
|
||||
"@vue/eslint-config-typescript": "^11.0.0",
|
||||
"@vue/runtime-core": "^3.2.45",
|
||||
"@vue/tsconfig": "^0.4.0",
|
||||
"eslint": "^8.22.0",
|
||||
"eslint-plugin-vue": "^9.3.0",
|
||||
"husky": "^8.0.0",
|
||||
"lint-staged": "^13.0.3",
|
||||
"miniprogram-api-typings": "^3.12.0",
|
||||
"prettier": "^2.7.1",
|
||||
"sass": "^1.56.1",
|
||||
"typescript": "^5.1.6",
|
||||
"vite": "^4.0.3",
|
||||
"vue-tsc": "^1.8.8"
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
{
|
||||
"appid": "wx8481e8716cf8d9e9",
|
||||
"compileType": "miniprogram",
|
||||
"libVersion": "3.3.0",
|
||||
"packOptions": {
|
||||
"ignore": [],
|
||||
"include": []
|
||||
},
|
||||
"setting": {
|
||||
"coverView": true,
|
||||
"es6": true,
|
||||
"postcss": true,
|
||||
"minified": true,
|
||||
"enhance": true,
|
||||
"showShadowRootInWxmlPanel": true,
|
||||
"packNpmRelationList": [],
|
||||
"babelSetting": {
|
||||
"ignore": [],
|
||||
"disablePlugins": [],
|
||||
"outputPath": ""
|
||||
}
|
||||
},
|
||||
"condition": {},
|
||||
"editorSetting": {
|
||||
"tabIndent": "insertSpaces",
|
||||
"tabSize": 4
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import { onLaunch, onShow, onHide } from '@dcloudio/uni-app'
|
||||
|
||||
onLaunch(() => {
|
||||
console.log('App Launch')
|
||||
})
|
||||
onShow(() => {
|
||||
console.log('App Show')
|
||||
})
|
||||
onHide(() => {
|
||||
console.log('App Hide')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
// 字体图标
|
||||
@import '@/styles/fonts.scss';
|
||||
@import '@/styles/base.scss';
|
||||
</style>
|
@ -0,0 +1,152 @@
|
||||
<script setup lang="ts">
|
||||
import { getHomeGoodsGuessLikeAPI } from '@/services/home'
|
||||
import type { PageParams } from '@/types/global'
|
||||
import type { GuessItem } from '@/types/home'
|
||||
import { onMounted, ref } from 'vue'
|
||||
|
||||
// 分页参数
|
||||
const pageParams: Required<PageParams> = {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
}
|
||||
// 猜你喜欢的列表
|
||||
const guessList = ref<GuessItem[]>([])
|
||||
// 已结束标记
|
||||
const finish = ref(false)
|
||||
// 获取猜你喜欢数据
|
||||
const getHomeGoodsGuessLikeData = async () => {
|
||||
// 退出分页判断
|
||||
if (finish.value === true) {
|
||||
return uni.showToast({ icon: 'none', title: '没有更多数据~' })
|
||||
}
|
||||
const res = await getHomeGoodsGuessLikeAPI(pageParams)
|
||||
// guessList.value = res.result.items
|
||||
// 数组追加
|
||||
guessList.value.push(...res.result.items)
|
||||
// 分页条件
|
||||
if (pageParams.page < res.result.pages) {
|
||||
// 页码累加
|
||||
pageParams.page++
|
||||
} else {
|
||||
finish.value = true
|
||||
}
|
||||
}
|
||||
// 重置数据
|
||||
const resetData = () => {
|
||||
pageParams.page = 1
|
||||
guessList.value = []
|
||||
finish.value = false
|
||||
}
|
||||
// 组件挂载完毕
|
||||
onMounted(() => {
|
||||
getHomeGoodsGuessLikeData()
|
||||
})
|
||||
// 暴露方法
|
||||
defineExpose({
|
||||
resetData,
|
||||
getMore: getHomeGoodsGuessLikeData,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 猜你喜欢 -->
|
||||
<view class="caption">
|
||||
<text class="text">猜你喜欢</text>
|
||||
</view>
|
||||
<view class="guess">
|
||||
<navigator
|
||||
class="guess-item"
|
||||
v-for="item in guessList"
|
||||
:key="item.id"
|
||||
:url="`/pages/goods/goods?id=${item.id}`"
|
||||
>
|
||||
<image class="image" mode="aspectFill" :src="item.picture"></image>
|
||||
<view class="name"> {{ item.name }} </view>
|
||||
<view class="price">
|
||||
<text class="small">¥</text>
|
||||
<text>{{ item.price }}</text>
|
||||
</view>
|
||||
</navigator>
|
||||
</view>
|
||||
<view class="loading-text">
|
||||
{{ finish ? '没有更多数据~' : '正在加载...' }}
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
/* 分类标题 */
|
||||
.caption {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
line-height: 1;
|
||||
padding: 36rpx 0 40rpx;
|
||||
font-size: 32rpx;
|
||||
color: #262626;
|
||||
.text {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0 28rpx 0 30rpx;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background-image: url(@/static/images/bubble.png);
|
||||
background-size: contain;
|
||||
margin: 0 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 猜你喜欢 */
|
||||
.guess {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
.guess-item {
|
||||
width: 345rpx;
|
||||
padding: 24rpx 20rpx 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
border-radius: 10rpx;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
}
|
||||
.image {
|
||||
width: 304rpx;
|
||||
height: 304rpx;
|
||||
}
|
||||
.name {
|
||||
height: 75rpx;
|
||||
margin: 10rpx 0;
|
||||
font-size: 26rpx;
|
||||
color: #262626;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
.price {
|
||||
line-height: 1;
|
||||
padding-top: 4rpx;
|
||||
color: #cf4444;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
.small {
|
||||
font-size: 80%;
|
||||
}
|
||||
}
|
||||
// 加载提示文字
|
||||
.loading-text {
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,40 @@
|
||||
<script setup lang="ts">
|
||||
import type { BannerItem } from '@/types/home'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const activeIndex = ref(0)
|
||||
|
||||
// 当 swiper 下标发生变化时触发
|
||||
const onChange: UniHelper.SwiperOnChange = (ev) => {
|
||||
activeIndex.value = ev.detail.current
|
||||
}
|
||||
// 定义 props 接收
|
||||
defineProps<{
|
||||
list: BannerItem[]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="carousel">
|
||||
<swiper :circular="true" :autoplay="false" :interval="3000" @change="onChange">
|
||||
<swiper-item v-for="item in list" :key="item.id">
|
||||
<navigator url="/pages/index/index" hover-class="none" class="navigator">
|
||||
<image mode="aspectFill" class="image" :src="item.imgUrl"></image>
|
||||
</navigator>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<!-- 指示点 -->
|
||||
<view class="indicator">
|
||||
<text
|
||||
v-for="(item, index) in list"
|
||||
:key="item.id"
|
||||
class="dot"
|
||||
:class="{ active: index === activeIndex }"
|
||||
></text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import './styles/XtxSwiper.scss';
|
||||
</style>
|
@ -0,0 +1,31 @@
|
||||
/* 轮播图 */
|
||||
.carousel {
|
||||
height: 280rpx;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transform: translateY(0);
|
||||
background-color: #efefef;
|
||||
.indicator {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 16rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
.dot {
|
||||
width: 30rpx;
|
||||
height: 6rpx;
|
||||
margin: 0 8rpx;
|
||||
border-radius: 6rpx;
|
||||
background-color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
.active {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
.navigator,
|
||||
.image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import type { XtxGuessInstance } from '@/types/components'
|
||||
import { ref } from 'vue'
|
||||
|
||||
/**
|
||||
* 猜你喜欢组合式函数
|
||||
*/
|
||||
export const useGuessList = () => {
|
||||
// 获取猜你喜欢组件实例
|
||||
const guessRef = ref<XtxGuessInstance>()
|
||||
|
||||
// 滚动触底事件
|
||||
const onScrolltolower = () => {
|
||||
guessRef.value?.getMore()
|
||||
}
|
||||
|
||||
// 返回 ref 和事件处理函数
|
||||
return {
|
||||
guessRef,
|
||||
onScrolltolower,
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import { DefineComponent } from 'vue'
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import { createSSRApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
|
||||
// 导入 pinia 实例
|
||||
import pinia from './stores'
|
||||
|
||||
export function createApp() {
|
||||
// 创建 vue 实例
|
||||
const app = createSSRApp(App)
|
||||
|
||||
// 使用 pinia
|
||||
app.use(pinia)
|
||||
|
||||
return {
|
||||
app,
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
{
|
||||
"name": "",
|
||||
"appid": "__UNI__265C604",
|
||||
"description": "",
|
||||
"versionName": "1.0.0",
|
||||
"versionCode": "100",
|
||||
"transformPx": false,
|
||||
/* 5+App特有相关 */
|
||||
"app-plus": {
|
||||
// 忽略版本检查提示框
|
||||
"compatible": {
|
||||
"ignoreVersion": true
|
||||
},
|
||||
"usingComponents": true,
|
||||
"nvueStyleCompiler": "uni-app",
|
||||
"compilerVersion": 3,
|
||||
"splashscreen": {
|
||||
"alwaysShowBeforeRender": true,
|
||||
"waiting": true,
|
||||
"autoclose": true,
|
||||
"delay": 0
|
||||
},
|
||||
/* 模块配置 */
|
||||
"modules": {},
|
||||
/* 应用发布信息 */
|
||||
"distribute": {
|
||||
/* android打包配置 */
|
||||
"android": {
|
||||
"permissions": [
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||
]
|
||||
},
|
||||
/* ios打包配置 */
|
||||
"ios": {
|
||||
"dSYMs": false
|
||||
},
|
||||
/* SDK配置 */
|
||||
"sdkConfigs": {
|
||||
"ad": {}
|
||||
},
|
||||
"icons": {
|
||||
"android": {
|
||||
"hdpi": "unpackage/res/icons/72x72.png",
|
||||
"xhdpi": "unpackage/res/icons/96x96.png",
|
||||
"xxhdpi": "unpackage/res/icons/144x144.png",
|
||||
"xxxhdpi": "unpackage/res/icons/192x192.png"
|
||||
},
|
||||
"ios": {
|
||||
"appstore": "unpackage/res/icons/1024x1024.png",
|
||||
"ipad": {
|
||||
"app": "unpackage/res/icons/76x76.png",
|
||||
"app@2x": "unpackage/res/icons/152x152.png",
|
||||
"notification": "unpackage/res/icons/20x20.png",
|
||||
"notification@2x": "unpackage/res/icons/40x40.png",
|
||||
"proapp@2x": "unpackage/res/icons/167x167.png",
|
||||
"settings": "unpackage/res/icons/29x29.png",
|
||||
"settings@2x": "unpackage/res/icons/58x58.png",
|
||||
"spotlight": "unpackage/res/icons/40x40.png",
|
||||
"spotlight@2x": "unpackage/res/icons/80x80.png"
|
||||
},
|
||||
"iphone": {
|
||||
"app@2x": "unpackage/res/icons/120x120.png",
|
||||
"app@3x": "unpackage/res/icons/180x180.png",
|
||||
"notification@2x": "unpackage/res/icons/40x40.png",
|
||||
"notification@3x": "unpackage/res/icons/60x60.png",
|
||||
"settings@2x": "unpackage/res/icons/58x58.png",
|
||||
"settings@3x": "unpackage/res/icons/87x87.png",
|
||||
"spotlight@2x": "unpackage/res/icons/80x80.png",
|
||||
"spotlight@3x": "unpackage/res/icons/120x120.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/* 快应用特有相关 */
|
||||
"quickapp": {},
|
||||
/* 网页端特有配置 */
|
||||
"h5": {
|
||||
"router": {
|
||||
// 路由基础路径。默认为 /
|
||||
"base": "./"
|
||||
}
|
||||
},
|
||||
/* 小程序特有相关 */
|
||||
"mp-weixin": {
|
||||
"appid": "wx26729f20b9efae3a",
|
||||
"setting": {
|
||||
"minified": true,
|
||||
"urlCheck": true
|
||||
},
|
||||
// 启用组件按需注入
|
||||
"lazyCodeLoading": "requiredComponents",
|
||||
"usingComponents": true
|
||||
},
|
||||
"mp-alipay": {
|
||||
"usingComponents": true
|
||||
},
|
||||
"mp-baidu": {
|
||||
"usingComponents": true
|
||||
},
|
||||
"mp-toutiao": {
|
||||
"usingComponents": true
|
||||
},
|
||||
"uniStatistics": {
|
||||
"enable": false
|
||||
},
|
||||
"vueVersion": "3"
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import CartMain from './components/CartMain.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CartMain />
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import CartMain from './components/CartMain.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 适配底部安全区 -->
|
||||
<CartMain safe-area-inset-bottom />
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,138 @@
|
||||
page {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.viewport {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.search {
|
||||
padding: 20rpx 30rpx;
|
||||
background-color: #fff;
|
||||
.input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 64rpx;
|
||||
padding-left: 26rpx;
|
||||
color: #8b8b8b;
|
||||
font-size: 28rpx;
|
||||
border-radius: 32rpx;
|
||||
background-color: #f3f4f4;
|
||||
}
|
||||
}
|
||||
.icon-search {
|
||||
&::before {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
}
|
||||
/* 分类 */
|
||||
.categories {
|
||||
flex: 1;
|
||||
min-height: 400rpx;
|
||||
display: flex;
|
||||
}
|
||||
/* 一级分类 */
|
||||
.primary {
|
||||
overflow: hidden;
|
||||
width: 180rpx;
|
||||
flex: none;
|
||||
background-color: #f6f6f6;
|
||||
.item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 96rpx;
|
||||
font-size: 26rpx;
|
||||
color: #595c63;
|
||||
position: relative;
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 42rpx;
|
||||
bottom: 0;
|
||||
width: 96rpx;
|
||||
border-top: 1rpx solid #e3e4e7;
|
||||
}
|
||||
}
|
||||
.active {
|
||||
background-color: #fff;
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 8rpx;
|
||||
height: 100%;
|
||||
background-color: #27ba9b;
|
||||
}
|
||||
}
|
||||
}
|
||||
.primary .item:last-child::after,
|
||||
.primary .active::after {
|
||||
display: none;
|
||||
}
|
||||
/* 二级分类 */
|
||||
.secondary {
|
||||
background-color: #fff;
|
||||
.carousel {
|
||||
height: 200rpx;
|
||||
margin: 0 30rpx 20rpx;
|
||||
border-radius: 4rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
.panel {
|
||||
margin: 0 30rpx 0rpx;
|
||||
}
|
||||
.title {
|
||||
height: 60rpx;
|
||||
line-height: 60rpx;
|
||||
color: #333;
|
||||
font-size: 28rpx;
|
||||
border-bottom: 1rpx solid #f7f7f8;
|
||||
.more {
|
||||
float: right;
|
||||
padding-left: 20rpx;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
.more {
|
||||
&::after {
|
||||
font-family: 'erabbit' !important;
|
||||
content: '\e6c2';
|
||||
}
|
||||
}
|
||||
.section {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 20rpx 0;
|
||||
.goods {
|
||||
width: 150rpx;
|
||||
margin: 0rpx 20rpx 20rpx 0;
|
||||
&:nth-child(3n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
.image {
|
||||
width: 150rpx;
|
||||
height: 150rpx;
|
||||
}
|
||||
.name {
|
||||
padding: 5rpx;
|
||||
font-size: 22rpx;
|
||||
color: #333;
|
||||
}
|
||||
.price {
|
||||
padding: 5rpx;
|
||||
font-size: 18rpx;
|
||||
color: #cf4444;
|
||||
}
|
||||
.number {
|
||||
font-size: 24rpx;
|
||||
margin-left: 2rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
<script setup lang="ts">
|
||||
//
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="address-panel">
|
||||
<!-- 关闭按钮 -->
|
||||
<text class="close icon-close"></text>
|
||||
<!-- 标题 -->
|
||||
<view class="title">配送至</view>
|
||||
<!-- 内容 -->
|
||||
<view class="content">
|
||||
<view class="item">
|
||||
<view class="user">李明 13824686868</view>
|
||||
<view class="address">北京市顺义区后沙峪地区安平北街6号院</view>
|
||||
<text class="icon icon-checked"></text>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="user">王东 13824686868</view>
|
||||
<view class="address">北京市顺义区后沙峪地区安平北街6号院</view>
|
||||
<text class="icon icon-ring"></text>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="user">张三 13824686868</view>
|
||||
<view class="address">北京市朝阳区孙河安平北街6号院</view>
|
||||
<text class="icon icon-ring"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="footer">
|
||||
<view class="button primary"> 新建地址 </view>
|
||||
<view v-if="false" class="button primary">确定</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.address-panel {
|
||||
padding: 0 30rpx;
|
||||
border-radius: 10rpx 10rpx 0 0;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.title {
|
||||
line-height: 1;
|
||||
padding: 40rpx 0;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: normal;
|
||||
border-bottom: 1rpx solid #ddd;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
right: 24rpx;
|
||||
top: 24rpx;
|
||||
}
|
||||
|
||||
.content {
|
||||
min-height: 300rpx;
|
||||
max-height: 540rpx;
|
||||
overflow: auto;
|
||||
padding: 20rpx;
|
||||
.item {
|
||||
padding: 30rpx 50rpx 30rpx 60rpx;
|
||||
background-size: 40rpx;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 center;
|
||||
background-image: url(https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/images/locate.png);
|
||||
position: relative;
|
||||
}
|
||||
.icon {
|
||||
color: #999;
|
||||
font-size: 40rpx;
|
||||
transform: translateY(-50%);
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
}
|
||||
.icon-checked {
|
||||
color: #27ba9b;
|
||||
}
|
||||
.icon-ring {
|
||||
color: #444;
|
||||
}
|
||||
.user {
|
||||
font-size: 28rpx;
|
||||
color: #444;
|
||||
font-weight: 500;
|
||||
}
|
||||
.address {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 0 40rpx;
|
||||
font-size: 28rpx;
|
||||
color: #444;
|
||||
|
||||
.button {
|
||||
flex: 1;
|
||||
height: 72rpx;
|
||||
text-align: center;
|
||||
line-height: 72rpx;
|
||||
margin: 0 20rpx;
|
||||
color: #fff;
|
||||
border-radius: 72rpx;
|
||||
}
|
||||
|
||||
.primary {
|
||||
color: #fff;
|
||||
background-color: #27ba9b;
|
||||
}
|
||||
|
||||
.secondary {
|
||||
background-color: #ffa868;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,41 @@
|
||||
<script setup lang="ts">
|
||||
import type { CategoryItem } from '@/types/home'
|
||||
|
||||
// 定义 props 接收数据
|
||||
defineProps<{
|
||||
list: CategoryItem[]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="category">
|
||||
|
||||
<view class="module-title">
|
||||
预约上门回收
|
||||
</view>
|
||||
|
||||
<view class="cont">
|
||||
<navigator
|
||||
class="category-item"
|
||||
hover-class="none"
|
||||
url="/pages/index/index"
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
>
|
||||
<view class="left">
|
||||
<view class="title">{{ item.name }}</view>
|
||||
<view class="desc">{{ item.desc }}</view>
|
||||
</view>
|
||||
<view class="right">
|
||||
<image class="icon" :src="item.icon"></image>
|
||||
</view>
|
||||
</navigator>
|
||||
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../styles/category.scss';
|
||||
</style>
|
@ -0,0 +1,71 @@
|
||||
<script setup lang="ts">
|
||||
// 获取屏幕边界到安全区域距离
|
||||
const { safeAreaInsets } = uni.getSystemInfoSync()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="navbar" :style="{ paddingTop: safeAreaInsets!.top + 10 + 'px' }">
|
||||
<!-- logo文字 -->
|
||||
<view class="logo">
|
||||
<image class="logo-image" src="@/static/images/logo.png"></image>
|
||||
<!-- <text class="logo-text">新鲜 · 亲民 · 快捷</text> -->
|
||||
</view>
|
||||
<!-- 搜索条 -->
|
||||
<view class="search">
|
||||
<text class="icon-search">搜索商品</text>
|
||||
<text class="icon-scan"></text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
/* 自定义导航条 */
|
||||
.navbar {
|
||||
background-image: url(@/static/images/navigator_bg.png);
|
||||
background-size: cover;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 20px;
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 64rpx;
|
||||
padding-left: 30rpx;
|
||||
.logo-image {
|
||||
width: 166rpx;
|
||||
height: 39rpx;
|
||||
}
|
||||
.logo-text {
|
||||
flex: 1;
|
||||
line-height: 28rpx;
|
||||
color: #fff;
|
||||
margin: 2rpx 0 0 20rpx;
|
||||
padding-left: 20rpx;
|
||||
border-left: 1rpx solid #fff;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
.search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 10rpx 0 26rpx;
|
||||
height: 64rpx;
|
||||
margin: 16rpx 20rpx;
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
border-radius: 32rpx;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
.icon-search {
|
||||
&::before {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
}
|
||||
.icon-scan {
|
||||
font-size: 30rpx;
|
||||
padding: 15rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,33 @@
|
||||
<script setup lang="ts">
|
||||
import type { HotItem } from '@/types/home'
|
||||
|
||||
// 定义 props 接收数据
|
||||
defineProps<{
|
||||
list: HotItem[]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 推荐专区 -->
|
||||
<view class="panel hot">
|
||||
<view class="item" v-for="item in list" :key="item.id">
|
||||
<view class="title">
|
||||
<text class="title-text">{{ item.title }}</text>
|
||||
<text class="title-desc">{{ item.alt }}</text>
|
||||
</view>
|
||||
<navigator hover-class="none" :url="`/pages/hot/hot?type=${item.type}`" class="cards">
|
||||
<image
|
||||
v-for="src in item.pictures"
|
||||
:key="src"
|
||||
class="image"
|
||||
mode="aspectFit"
|
||||
:src="src"
|
||||
></image>
|
||||
</navigator>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../styles/hot.scss';
|
||||
</style>
|
@ -0,0 +1,156 @@
|
||||
<script setup lang="ts">
|
||||
import { getHomeBannerAPI, getHomeCategoryAPI, getHomeHotAPI } from '@/services/home'
|
||||
import type { BannerItem, CategoryItem, HotItem } from '@/types/home'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { ref } from 'vue'
|
||||
import CustomNavbar from './components/CustomNavbar.vue'
|
||||
import CategoryPanel from './components/CategoryPanel.vue'
|
||||
import HotPanel from './components/HotPanel.vue'
|
||||
import PageSkeleton from './components/PageSkeleton.vue'
|
||||
import { useGuessList } from '@/composables'
|
||||
|
||||
// 获取轮播图数据
|
||||
const bannerList = ref<BannerItem[]>([])
|
||||
const getHomeBannerData = async () => {
|
||||
const res = await getHomeBannerAPI()
|
||||
bannerList.value = res.result
|
||||
}
|
||||
|
||||
// 获取前台分类数据
|
||||
const categoryList = ref<CategoryItem[]>([])
|
||||
const getHomeCategoryData = async () => {
|
||||
// const res = await getHomeCategoryAPI()
|
||||
// console.log(JSON.stringify(res.result,null, 4))
|
||||
// debugger
|
||||
// categoryList.value = res.result
|
||||
|
||||
const data = [
|
||||
{
|
||||
"id": "1019000",
|
||||
"name": "旧衣服",
|
||||
"desc": '旧衣服回收不浪费',
|
||||
"icon": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-05-06/4b02f01f-a365-4b6c-9f7a-8b0f591dda02.png?quality=95&imageView"
|
||||
},
|
||||
{
|
||||
"id": "1043000",
|
||||
"name": "家用电器",
|
||||
"desc": '旧衣服回收不浪费',
|
||||
"icon": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-05-06/9660870d-6a59-4624-8064-b3a8cbf50d5c.png?quality=95&imageView"
|
||||
},
|
||||
{
|
||||
"id": "109243029",
|
||||
"name": "废纸废书",
|
||||
"desc": '旧衣服回收不浪费',
|
||||
"icon": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-05-06/7d19752c-baff-49b6-bd02-5ece1d729214.png?quality=95&imageView"
|
||||
},
|
||||
{
|
||||
"id": "19999999",
|
||||
"name": "废金属",
|
||||
"desc": '旧衣服回收不浪费',
|
||||
"icon": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-05-06/4ff20b9e-8150-4bd3-87a3-0cd6766938dd.png?quality=95&imageView"
|
||||
},
|
||||
{
|
||||
"id": "999999",
|
||||
"name": "废玻璃",
|
||||
"desc": '旧衣服回收不浪费',
|
||||
"icon": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-05-06/d9ee4919-0d2c-4383-9916-2dd25f12610c.png?quality=95&imageView"
|
||||
},
|
||||
{
|
||||
"id": "999999",
|
||||
"name": "废塑料",
|
||||
"desc": '旧衣服回收不浪费',
|
||||
"icon": "http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-05-06/d9ee4919-0d2c-4383-9916-2dd25f12610c.png?quality=95&imageView"
|
||||
}
|
||||
]
|
||||
categoryList.value = data
|
||||
}
|
||||
|
||||
// 获取热门推荐数据
|
||||
const hotList = ref<HotItem[]>([])
|
||||
const getHomeHotData = async () => {
|
||||
const res = await getHomeHotAPI()
|
||||
hotList.value = res.result
|
||||
}
|
||||
|
||||
// 是否加载中标记
|
||||
const isLoading = ref(false)
|
||||
|
||||
// 页面加载
|
||||
onLoad(async () => {
|
||||
isLoading.value = true
|
||||
await Promise.all([getHomeBannerData(), getHomeCategoryData(), getHomeHotData()])
|
||||
isLoading.value = false
|
||||
})
|
||||
|
||||
// 猜你喜欢组合式函数调用
|
||||
const { guessRef, onScrolltolower } = useGuessList()
|
||||
// 当前下拉刷新状态
|
||||
const isTriggered = ref(false)
|
||||
// 自定义下拉刷新被触发
|
||||
const onRefresherrefresh = async () => {
|
||||
// 开始动画
|
||||
isTriggered.value = true
|
||||
// 加载数据
|
||||
// await getHomeBannerData()
|
||||
// await getHomeCategoryData()
|
||||
// await getHomeHotData()
|
||||
// 重置猜你喜欢组件数据
|
||||
guessRef.value?.resetData()
|
||||
await Promise.all([
|
||||
getHomeBannerData(),
|
||||
getHomeCategoryData(),
|
||||
getHomeHotData(),
|
||||
guessRef.value?.getMore(),
|
||||
])
|
||||
// 关闭动画
|
||||
isTriggered.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="viewport">
|
||||
<!-- 自定义导航栏 -->
|
||||
<CustomNavbar />
|
||||
<!-- 滚动容器 -->
|
||||
<scroll-view
|
||||
enable-back-to-top
|
||||
refresher-enabled
|
||||
@refresherrefresh="onRefresherrefresh"
|
||||
:refresher-triggered="isTriggered"
|
||||
@scrolltolower="onScrolltolower"
|
||||
class="scroll-view"
|
||||
scroll-y
|
||||
>
|
||||
<PageSkeleton v-if="isLoading" />
|
||||
<template v-else>
|
||||
<!-- 自定义轮播图 -->
|
||||
<XtxSwiper :list="bannerList" />
|
||||
<!-- 分类面板 -->
|
||||
<CategoryPanel :list="categoryList" />
|
||||
<!-- 热门推荐 -->
|
||||
<HotPanel :list="hotList" />
|
||||
<!-- 猜你喜欢 -->
|
||||
<XtxGuess ref="guessRef" />
|
||||
</template>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f7f7f7;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.viewport {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.scroll-view {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,46 @@
|
||||
/* 前台类目 */
|
||||
|
||||
.category {
|
||||
.module-title {
|
||||
margin: 32rpx 32rpx 0;
|
||||
font-size: 30rpx;
|
||||
color: #0D0D26;
|
||||
padding-bottom: 20rpx;
|
||||
border-bottom: 2rpx solid #F2F2F2;
|
||||
}
|
||||
.cont {
|
||||
// margin: 20rpx 0 0;
|
||||
padding: 10rpx 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
min-height: 328rpx;
|
||||
.category-item {
|
||||
width: 48%;
|
||||
border-radius: 16rpx;
|
||||
margin: 8rpx;
|
||||
padding: 16rpx 0;
|
||||
display: flex;
|
||||
justify-content: center; // flex-direction: row;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
background: #FCF4FB;
|
||||
.left {
|
||||
.title {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
.desc {
|
||||
font-size: 24rpx;
|
||||
color: #7BAFBD;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
.icon {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
@charset "UTF-8";
|
||||
/* 前台类目 */
|
||||
.category .module-title {
|
||||
margin: 32rpx 32rpx 0;
|
||||
font-size: 30rpx;
|
||||
color: #0D0D26;
|
||||
padding-bottom: 20rpx;
|
||||
border-bottom: 2rpx solid #F2F2F2;
|
||||
}
|
||||
|
||||
.category .cont {
|
||||
padding: 10rpx 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
min-height: 328rpx;
|
||||
}
|
||||
|
||||
.category .cont .category-item {
|
||||
width: 48%;
|
||||
border-radius: 16rpx;
|
||||
margin: 8rpx;
|
||||
padding: 16rpx 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
background: #FCF4FB;
|
||||
}
|
||||
|
||||
.category .cont .category-item .left .title {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.category .cont .category-item .left .desc {
|
||||
font-size: 24rpx;
|
||||
color: #7BAFBD;
|
||||
}
|
||||
|
||||
.category .cont .category-item .right .icon {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/* 热门推荐 */
|
||||
|
||||
.hot {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
min-height: 508rpx;
|
||||
margin: 20rpx 20rpx 0;
|
||||
border-radius: 10rpx;
|
||||
background-color: #fff;
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx 24rpx 0;
|
||||
font-size: 32rpx;
|
||||
color: #262626;
|
||||
position: relative;
|
||||
.title-desc {
|
||||
font-size: 24rpx;
|
||||
color: #7f7f7f;
|
||||
margin-left: 18rpx;
|
||||
}
|
||||
}
|
||||
.item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 50%;
|
||||
height: 254rpx;
|
||||
border-right: 1rpx solid #eee;
|
||||
border-top: 1rpx solid #eee;
|
||||
.title {
|
||||
justify-content: start;
|
||||
}
|
||||
&:nth-child(2n) {
|
||||
border-right: 0 none;
|
||||
}
|
||||
&:nth-child(-n + 2) {
|
||||
border-top: 0 none;
|
||||
}
|
||||
.image {
|
||||
width: 150rpx;
|
||||
height: 150rpx;
|
||||
}
|
||||
}
|
||||
.cards {
|
||||
flex: 1;
|
||||
padding: 15rpx 20rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
@charset "UTF-8";
|
||||
/* 热门推荐 */
|
||||
.hot {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
min-height: 508rpx;
|
||||
margin: 20rpx 20rpx 0;
|
||||
border-radius: 10rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.hot .title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx 24rpx 0;
|
||||
font-size: 32rpx;
|
||||
color: #262626;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.hot .title .title-desc {
|
||||
font-size: 24rpx;
|
||||
color: #7f7f7f;
|
||||
margin-left: 18rpx;
|
||||
}
|
||||
|
||||
.hot .item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 50%;
|
||||
height: 254rpx;
|
||||
border-right: 1rpx solid #eee;
|
||||
border-top: 1rpx solid #eee;
|
||||
}
|
||||
|
||||
.hot .item .title {
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.hot .item:nth-child(2n) {
|
||||
border-right: 0 none;
|
||||
}
|
||||
|
||||
.hot .item:nth-child(-n + 2) {
|
||||
border-top: 0 none;
|
||||
}
|
||||
|
||||
.hot .item .image {
|
||||
width: 150rpx;
|
||||
height: 150rpx;
|
||||
}
|
||||
|
||||
.hot .cards {
|
||||
flex: 1;
|
||||
padding: 15rpx 20rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
<script setup lang="ts">
|
||||
import { deleteMemberAddressByIdAPI, getMemberAddressAPI } from '@/services/address'
|
||||
import { useAddressStore } from '@/stores/modules/address'
|
||||
import type { AddressItem } from '@/types/address'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { ref } from 'vue'
|
||||
|
||||
// 获取收货地址列表数据
|
||||
const addressList = ref<AddressItem[]>([])
|
||||
const getMemberAddressData = async () => {
|
||||
const res = await getMemberAddressAPI()
|
||||
addressList.value = res.result
|
||||
}
|
||||
|
||||
// 初始化调用(页面显示)
|
||||
onShow(() => {
|
||||
getMemberAddressData()
|
||||
})
|
||||
|
||||
// 删除收货地址
|
||||
const onDeleteAddress = (id: string) => {
|
||||
// 二次确认
|
||||
uni.showModal({
|
||||
content: '删除地址?',
|
||||
confirmColor: '#27BA9B',
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
// 根据id删除收货地址
|
||||
await deleteMemberAddressByIdAPI(id)
|
||||
// 重新获取收货地址列表
|
||||
getMemberAddressData()
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 修改收货地址
|
||||
const onChangeAddress = (item: AddressItem) => {
|
||||
// 修改地址
|
||||
const addressStore = useAddressStore()
|
||||
addressStore.changeSelectedAddress(item)
|
||||
// 返回上一页
|
||||
uni.navigateBack()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="viewport">
|
||||
<!-- 地址列表 -->
|
||||
<scroll-view enable-back-to-top class="scroll-view" scroll-y>
|
||||
<view v-if="addressList.length" class="address">
|
||||
<uni-swipe-action class="address-list">
|
||||
<!-- 收货地址项 -->
|
||||
<uni-swipe-action-item class="item" v-for="item in addressList" :key="item.id">
|
||||
<view class="item-content" @tap="onChangeAddress(item)">
|
||||
<view class="user">
|
||||
{{ item.receiver }}
|
||||
<text class="contact">{{ item.contact }}</text>
|
||||
<text v-if="item.isDefault" class="badge">默认</text>
|
||||
</view>
|
||||
<view class="locate">{{ item.fullLocation }} {{ item.address }}</view>
|
||||
<!-- H5 端需添加 .prevent 阻止链接的默认行为 -->
|
||||
<navigator
|
||||
class="edit"
|
||||
hover-class="none"
|
||||
:url="`/pagesMember/address-form/address-form?id=${item.id}`"
|
||||
@tap.stop="() => {}"
|
||||
@tap.prevent="() => {}"
|
||||
>
|
||||
修改
|
||||
</navigator>
|
||||
</view>
|
||||
<!-- 右侧插槽 -->
|
||||
<template #right>
|
||||
<button @tap="onDeleteAddress(item.id)" class="delete-button">删除</button>
|
||||
</template>
|
||||
</uni-swipe-action-item>
|
||||
</uni-swipe-action>
|
||||
</view>
|
||||
<view v-else class="blank">暂无收货地址</view>
|
||||
</scroll-view>
|
||||
<!-- 添加按钮 -->
|
||||
<view class="add-btn">
|
||||
<navigator hover-class="none" url="/pagesMember/address-form/address-form">
|
||||
新建地址
|
||||
</navigator>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 删除按钮 */
|
||||
.delete-button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 50px;
|
||||
height: 100%;
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
border-radius: 0;
|
||||
padding: 0;
|
||||
background-color: #cf4444;
|
||||
}
|
||||
|
||||
.viewport {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background-color: #f4f4f4;
|
||||
|
||||
.scroll-view {
|
||||
padding-top: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.address {
|
||||
padding: 0 20rpx;
|
||||
margin: 0 20rpx;
|
||||
border-radius: 10rpx;
|
||||
background-color: #fff;
|
||||
|
||||
.item-content {
|
||||
line-height: 1;
|
||||
padding: 40rpx 10rpx 38rpx;
|
||||
border-bottom: 1rpx solid #ddd;
|
||||
position: relative;
|
||||
|
||||
.edit {
|
||||
position: absolute;
|
||||
top: 36rpx;
|
||||
right: 30rpx;
|
||||
padding: 2rpx 0 2rpx 20rpx;
|
||||
border-left: 1rpx solid #666;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.item:last-child .item-content {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.user {
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 20rpx;
|
||||
color: #333;
|
||||
|
||||
.contact {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 4rpx 10rpx 2rpx 14rpx;
|
||||
margin: 2rpx 0 0 10rpx;
|
||||
font-size: 26rpx;
|
||||
color: #27ba9b;
|
||||
border-radius: 6rpx;
|
||||
border: 1rpx solid #27ba9b;
|
||||
}
|
||||
}
|
||||
|
||||
.locate {
|
||||
line-height: 1.6;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.blank {
|
||||
margin-top: 300rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
height: 80rpx;
|
||||
text-align: center;
|
||||
line-height: 80rpx;
|
||||
margin: 30rpx 20rpx;
|
||||
color: #fff;
|
||||
border-radius: 80rpx;
|
||||
font-size: 30rpx;
|
||||
background-color: #27ba9b;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,108 @@
|
||||
<script setup lang="ts">
|
||||
import { useMemberStore } from '@/stores'
|
||||
|
||||
const memberStore = useMemberStore()
|
||||
// 退出登录
|
||||
const onLogout = () => {
|
||||
// 模态弹窗
|
||||
uni.showModal({
|
||||
content: '是否退出登录?',
|
||||
confirmColor: '#27BA9B',
|
||||
success: res => {
|
||||
if (res.confirm) {
|
||||
// 清理用户信息
|
||||
memberStore.clearProfile()
|
||||
// 返回上一页
|
||||
uni.navigateBack()
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="viewport">
|
||||
<!-- 列表1 -->
|
||||
<view class="list" v-if="memberStore.profile">
|
||||
<navigator url="/pagesMember/address/address" hover-class="none" class="item arrow">
|
||||
我的收货地址
|
||||
</navigator>
|
||||
</view>
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- 列表2 -->
|
||||
<view class="list">
|
||||
<button hover-class="none" class="item arrow" open-type="openSetting">授权管理</button>
|
||||
<button hover-class="none" class="item arrow" open-type="feedback">问题反馈</button>
|
||||
<button hover-class="none" class="item arrow" open-type="contact">联系我们</button>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- 操作按钮 -->
|
||||
<view class="action" v-if="memberStore.profile">
|
||||
<view @tap="onLogout" class="button">退出登录</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
|
||||
.viewport {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
/* 列表 */
|
||||
.list {
|
||||
padding: 0 20rpx;
|
||||
background-color: #fff;
|
||||
margin-bottom: 20rpx;
|
||||
border-radius: 10rpx;
|
||||
.item {
|
||||
line-height: 90rpx;
|
||||
padding-left: 10rpx;
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
border-top: 1rpx solid #ddd;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
border-radius: 0;
|
||||
background-color: #fff;
|
||||
&::after {
|
||||
width: auto;
|
||||
height: auto;
|
||||
left: auto;
|
||||
border: none;
|
||||
}
|
||||
&:first-child {
|
||||
border: none;
|
||||
}
|
||||
&::after {
|
||||
right: 5rpx;
|
||||
}
|
||||
}
|
||||
.arrow::after {
|
||||
content: '\e6c2';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
color: #ccc;
|
||||
font-family: 'erabbit' !important;
|
||||
font-size: 32rpx;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
/* 操作按钮 */
|
||||
.action {
|
||||
text-align: center;
|
||||
line-height: 90rpx;
|
||||
margin-top: 40rpx;
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
.button {
|
||||
background-color: #fff;
|
||||
margin-bottom: 20rpx;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,105 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import OrderList from './components/OrderList.vue'
|
||||
|
||||
// 获取页面参数
|
||||
const query = defineProps<{
|
||||
type: string
|
||||
}>()
|
||||
|
||||
// tabs 数据
|
||||
const orderTabs = ref([
|
||||
{ orderState: 0, title: '全部', isRender: false },
|
||||
{ orderState: 1, title: '待付款', isRender: false },
|
||||
{ orderState: 2, title: '待发货', isRender: false },
|
||||
{ orderState: 3, title: '待收货', isRender: false },
|
||||
{ orderState: 4, title: '待评价', isRender: false },
|
||||
])
|
||||
|
||||
// 高亮下标
|
||||
const activeIndex = ref(orderTabs.value.findIndex((v) => v.orderState === Number(query.type)))
|
||||
// 默认渲染容器
|
||||
orderTabs.value[activeIndex.value].isRender = true
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="viewport">
|
||||
<!-- tabs -->
|
||||
<view class="tabs">
|
||||
<text
|
||||
class="item"
|
||||
v-for="(item, index) in orderTabs"
|
||||
:key="item.title"
|
||||
@tap="
|
||||
() => {
|
||||
activeIndex = index
|
||||
item.isRender = true
|
||||
}
|
||||
"
|
||||
>
|
||||
{{ item.title }}
|
||||
</text>
|
||||
<!-- 游标 -->
|
||||
<view class="cursor" :style="{ left: activeIndex * 20 + '%' }"></view>
|
||||
</view>
|
||||
<!-- 滑动容器 -->
|
||||
<swiper class="swiper" :current="activeIndex" @change="activeIndex = $event.detail.current">
|
||||
<!-- 滑动项 -->
|
||||
<swiper-item v-for="item in orderTabs" :key="item.title">
|
||||
<!-- 订单列表 -->
|
||||
<OrderList v-if="item.isRender" :order-state="item.orderState" />
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.viewport {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
// tabs
|
||||
.tabs {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
line-height: 60rpx;
|
||||
margin: 0 10rpx;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 4rpx 6rpx rgba(240, 240, 240, 0.6);
|
||||
position: relative;
|
||||
z-index: 9;
|
||||
|
||||
.item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
.cursor {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 20%;
|
||||
height: 6rpx;
|
||||
padding: 0 50rpx;
|
||||
background-color: #27ba9b;
|
||||
/* 过渡效果 */
|
||||
transition: all 0.4s;
|
||||
}
|
||||
}
|
||||
|
||||
// swiper
|
||||
.swiper {
|
||||
background-color: #f7f7f8;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,59 @@
|
||||
import type { AddressItem, AddressParams } from '@/types/address'
|
||||
import { http } from '@/utils/http'
|
||||
|
||||
/**
|
||||
* 添加收货地址
|
||||
* @param data 请求参数
|
||||
*/
|
||||
export const postMemberAddressAPI = (data: AddressParams) => {
|
||||
return http({
|
||||
method: 'POST',
|
||||
url: '/member/address',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取收货地址列表
|
||||
*/
|
||||
export const getMemberAddressAPI = () => {
|
||||
return http<AddressItem[]>({
|
||||
method: 'GET',
|
||||
url: '/member/address',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取收货地址详情
|
||||
* @param id 地址id(路径参数)
|
||||
*/
|
||||
export const getMemberAddressByIdAPI = (id: string) => {
|
||||
return http<AddressItem>({
|
||||
method: 'GET',
|
||||
url: `/member/address/${id}`,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改收货地址
|
||||
* @param id 地址id(路径参数)
|
||||
* @param data 表单数据(请求体参数)
|
||||
*/
|
||||
export const putMemberAddressByIdAPI = (id: string, data: AddressParams) => {
|
||||
return http({
|
||||
method: 'PUT',
|
||||
url: `/member/address/${id}`,
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除收货地址
|
||||
* @param id 地址id(路径参数)
|
||||
*/
|
||||
export const deleteMemberAddressByIdAPI = (id: string) => {
|
||||
return http({
|
||||
method: 'DELETE',
|
||||
url: `/member/address/${id}`,
|
||||
})
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
import type { CartItem } from '@/types/cart'
|
||||
import { http } from '@/utils/http'
|
||||
/**
|
||||
* 加入购物车
|
||||
* @param data 请求体参数
|
||||
*/
|
||||
export const postMemberCartAPI = (data: { skuId: string; count: number }) => {
|
||||
return http({
|
||||
method: 'POST',
|
||||
url: '/member/cart',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取购物车列表
|
||||
*/
|
||||
export const getMemberCartAPI = () => {
|
||||
return http<CartItem[]>({
|
||||
method: 'GET',
|
||||
url: '/member/cart',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除/清空购物车单品
|
||||
* @param data 请求体参数 ids SKUID 集合
|
||||
*/
|
||||
export const deleteMemberCartAPI = (data: { ids: string[] }) => {
|
||||
return http({
|
||||
method: 'DELETE',
|
||||
url: '/member/cart',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改购物车单品
|
||||
* @param skuId SKUID
|
||||
* @param data selected 选中状态 count 商品数量
|
||||
*/
|
||||
export const putMemberCartBySkuIdAPI = (
|
||||
skuId: string,
|
||||
data: { selected?: boolean; count?: number },
|
||||
) => {
|
||||
return http({
|
||||
method: 'PUT',
|
||||
url: `/member/cart/${skuId}`,
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 购物车全选/取消全选
|
||||
* @param data selected 是否选中
|
||||
*/
|
||||
export const putMemberCartSelectedAPI = (data: { selected: boolean }) => {
|
||||
return http({
|
||||
method: 'PUT',
|
||||
url: '/member/cart/selected',
|
||||
data,
|
||||
})
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
import type { CategoryTopItem } from '@/types/category'
|
||||
import { http } from '@/utils/http'
|
||||
|
||||
/**
|
||||
* 分类列表-小程序
|
||||
*/
|
||||
export const getCategoryTopAPI = () => {
|
||||
return http<CategoryTopItem[]>({
|
||||
method: 'GET',
|
||||
url: '/category/top',
|
||||
})
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/** 订单状态枚举 */
|
||||
export enum OrderState {
|
||||
/** 待付款 */
|
||||
DaiFuKuan = 1,
|
||||
/** 待发货 */
|
||||
DaiFaHuo = 2,
|
||||
/** 待收货 */
|
||||
DaiShouHuo = 3,
|
||||
/** 待评价 */
|
||||
DaiPingJia = 4,
|
||||
/** 已完成 */
|
||||
YiWanCheng = 5,
|
||||
/** 已取消 */
|
||||
YiQuXiao = 6,
|
||||
}
|
||||
/** 订单状态列表 */
|
||||
export const orderStateList = [
|
||||
{ id: 0, text: '' },
|
||||
{ id: 1, text: '待付款' },
|
||||
{ id: 2, text: '待发货' },
|
||||
{ id: 3, text: '待收货' },
|
||||
{ id: 4, text: '待评价' },
|
||||
{ id: 5, text: '已完成' },
|
||||
{ id: 6, text: '已取消' },
|
||||
]
|
@ -0,0 +1,14 @@
|
||||
import type { GoodsResult } from '@/types/goods'
|
||||
import { http } from '@/utils/http'
|
||||
|
||||
/**
|
||||
* 商品详情
|
||||
* @param id 商品id
|
||||
*/
|
||||
export const getGoodsByIdAPI = (id: string) => {
|
||||
return http<GoodsResult>({
|
||||
method: 'GET',
|
||||
url: '/goods',
|
||||
data: { id },
|
||||
})
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import { http } from '@/utils/http'
|
||||
import type { PageParams } from '@/types/global'
|
||||
import type { HotResult } from '@/types/hot'
|
||||
|
||||
type HotParams = PageParams & { subType?: string }
|
||||
/**
|
||||
* 通用热门推荐类型
|
||||
* @param url 请求地址
|
||||
* @param data 请求参数
|
||||
*/
|
||||
export const getHotRecommendAPI = (url: string, data?: HotParams) => {
|
||||
return http<HotResult>({
|
||||
method: 'GET',
|
||||
url,
|
||||
data,
|
||||
})
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
import type { LoginResult } from '@/types/member'
|
||||
import { http } from '@/utils/http'
|
||||
|
||||
type LoginWxMinParams = {
|
||||
code: string
|
||||
encryptedData?: string
|
||||
iv?: string
|
||||
}
|
||||
/**
|
||||
* 小程序登录
|
||||
* @param data 请求参数
|
||||
*/
|
||||
export const postLoginWxMinAPI = (data: LoginWxMinParams) => {
|
||||
return http<LoginResult>({
|
||||
method: 'POST',
|
||||
url: '/login/wxMin',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序登录_内测版
|
||||
* @param phoneNumber 模拟手机号码
|
||||
*/
|
||||
export const postLoginWxMinSimpleAPI = (phoneNumber: string) => {
|
||||
return http<LoginResult>({
|
||||
method: 'POST',
|
||||
url: '/login/wxMin/simple',
|
||||
data: {
|
||||
phoneNumber,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
type LoginParams = {
|
||||
account: string
|
||||
password: string
|
||||
}
|
||||
/**
|
||||
* 传统登录-用户名+密码
|
||||
* @param data 请求参数
|
||||
*/
|
||||
export const postLoginAPI = (data: LoginParams) => {
|
||||
return http<LoginResult>({
|
||||
method: 'POST',
|
||||
url: '/login',
|
||||
data,
|
||||
})
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import { http } from '@/utils/http'
|
||||
|
||||
/**
|
||||
* 获取微信支付参数
|
||||
* @param data orderId 订单id
|
||||
*/
|
||||
export const getPayWxPayMiniPayAPI = (data: { orderId: string }) => {
|
||||
return http<WechatMiniprogram.RequestPaymentOption>({
|
||||
method: 'GET',
|
||||
url: '/pay/wxPay/miniPay',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟支付-内测版
|
||||
* @param data orderId 订单id
|
||||
*/
|
||||
export const getPayMockAPI = (data: { orderId: string }) => {
|
||||
return http({
|
||||
method: 'GET',
|
||||
url: '/pay/mock',
|
||||
data,
|
||||
})
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
import type { ProfileDetail, ProfileParams } from '@/types/member'
|
||||
import { http } from '@/utils/http'
|
||||
|
||||
/**
|
||||
* 获取个人信息
|
||||
*/
|
||||
export const getMemberProfileAPI = () => {
|
||||
return http<ProfileDetail>({
|
||||
method: 'GET',
|
||||
url: '/member/profile',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改个人信息
|
||||
* @param data 请求体参数
|
||||
*/
|
||||
export const putMemberProfileAPI = (data: ProfileParams) => {
|
||||
return http<ProfileDetail>({
|
||||
method: 'PUT',
|
||||
url: '/member/profile',
|
||||
data,
|
||||
})
|
||||
}
|
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 813 B |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 4.5 KiB |
@ -0,0 +1,13 @@
|
||||
import { createPinia } from 'pinia'
|
||||
import persist from 'pinia-plugin-persistedstate'
|
||||
|
||||
// 创建 pinia 实例
|
||||
const pinia = createPinia()
|
||||
// 使用持久化存储插件
|
||||
pinia.use(persist)
|
||||
|
||||
// 默认导出,给 main.ts 使用
|
||||
export default pinia
|
||||
|
||||
// 模块统一导出
|
||||
export * from './modules/member'
|
@ -0,0 +1,16 @@
|
||||
import type { AddressItem } from '@/types/address'
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export const useAddressStore = defineStore('address', () => {
|
||||
const selectedAddress = ref<AddressItem>()
|
||||
|
||||
const changeSelectedAddress = (val: AddressItem) => {
|
||||
selectedAddress.value = val
|
||||
}
|
||||
|
||||
return {
|
||||
selectedAddress,
|
||||
changeSelectedAddress,
|
||||
}
|
||||
})
|