Browse Source

250122

master
梁欣 4 months ago
commit
5c5591d581
  1. 24
      .gitignore
  2. 3
      .vscode/extensions.json
  3. 5
      README.md
  4. 20
      index.html
  5. 28
      package.json
  6. 1977
      pnpm-lock.yaml
  7. 6
      postcss.config.cjs
  8. BIN
      public/favicon.ico
  9. 1
      public/vite.svg
  10. 21
      src/App.vue
  11. 5
      src/api/admin.js
  12. 86
      src/api/base.js
  13. 25
      src/api/common.js
  14. 7
      src/api/deposit.js
  15. 4
      src/api/field.js
  16. 10
      src/api/icCard.js
  17. 10
      src/api/order.js
  18. 3
      src/api/print.js
  19. 4
      src/api/site.js
  20. 3
      src/api/thinkVip.js
  21. 3
      src/api/usersVip.js
  22. BIN
      src/assets/bg.jpg
  23. 1
      src/assets/vue.svg
  24. 68
      src/components/ChangePwd.vue
  25. 136
      src/components/Deposite.vue
  26. 80
      src/components/IcCardInfo.vue
  27. 296
      src/components/NewCard.vue
  28. 349
      src/components/OrderInfo.vue
  29. 38
      src/components/Page.vue
  30. 229
      src/components/ReNewCard.vue
  31. 397
      src/components/SelectSite.vue
  32. 11
      src/config/vite.build.js
  33. 14
      src/config/vite.dev.js
  34. 25
      src/main.js
  35. 118
      src/pages/Deposit.vue
  36. 142
      src/pages/IcCard.vue
  37. 237
      src/pages/Order.vue
  38. 130
      src/pages/ReNewIcCard.vue
  39. 126
      src/pages/UsersVip.vue
  40. 163
      src/pages/VipCardLog.vue
  41. 59
      src/pages/index/Index.vue
  42. 17
      src/router/index.js
  43. 10
      src/store/index.js
  44. 10
      src/style.css
  45. 205
      src/views/Home.vue
  46. 57
      src/views/Login.vue
  47. 8
      tailwind.config.cjs
  48. 15
      vite.base.config.js
  49. 19
      vite.config.js

24
.gitignore

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

3
.vscode/extensions.json

@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar"]
}

5
README.md

@ -0,0 +1,5 @@
# Vue 3 + Vite
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
Learn more about IDE Support for Vue in the [Vue Docs Scaling up Guide](https://vuejs.org/guide/scaling-up/tooling.html#ide-support).

20
index.html

@ -0,0 +1,20 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<link rel="icon" type="image/svg+xml" href="/public/favicon.ico"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>广西体育中心</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
<style>
html, body {
margin: 0;
padding: 0;
}
</style>

28
package.json

@ -0,0 +1,28 @@
{
"name": "gxtyzx-front",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --mode=dev",
"build": "vite build --mode=build",
"preview": "vite preview"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"axios": "^1.7.9",
"dayjs": "^1.11.13",
"element-plus": "^2.9.3",
"pinia": "^2.3.0",
"vite-plugin-compression": "^0.5.1",
"vue": "^3.5.13",
"vue-router": "4"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.1",
"autoprefixer": "^10.4.20",
"postcss": "^8.4.49",
"tailwindcss": "^3.4.17",
"vite": "^6.0.5"
}
}

1977
pnpm-lock.yaml

File diff suppressed because it is too large

6
postcss.config.cjs

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}

BIN
public/favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

1
public/vite.svg

@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 1.5 KiB

21
src/App.vue

@ -0,0 +1,21 @@
<template>
<div>
<Login v-if="currentPage === '/Login'"/>
<Home v-else/>
</div>
</template>
<script setup>
import {ref, watch} from 'vue'
import {useRoute} from 'vue-router'
import Login from '@/views/Login.vue'
import Home from '@/views/Home.vue'
const route = useRoute()
let currentPage = ref('/Index')
watch(route, (path) => {
currentPage.value = path.path
})
</script>

5
src/api/admin.js

@ -0,0 +1,5 @@
import post, {SERVER_API} from "@/api/base.js";
export const loginReq = data => post(`${SERVER_API}/login`, data)
export const changePwdReq = data => post(`${SERVER_API}/system/user/password`, data)

86
src/api/base.js

@ -0,0 +1,86 @@
import axios from "axios";
import {getUserInfo, clearUserInfo} from "./common.js";
import router from "@/router/index.js";
import {ElMessage} from "element-plus";
export const SERVER_API = process.env.VUE_APP_SERVER_API
axios.defaults.baseURL = process.env.VUE_APP_BASE_API
axios.interceptors.request.use(
config => {
const token = getUserInfo().token
if (token && token !== 'undefined') {
config.headers['Authorization'] = 'Bearer ' + token
} else {
if (config.url !== '/login') {
clearUserInfo()
router.push('Login')
}
}
config.headers.tenantId = 10150
return config
},
)
axios.interceptors.response.use(
res => {
if (parseInt(res.data.code) !== 0) {
ElMessage.closeAll()
ElMessage({
type: 'error',
message: res.data.message ?? '未知错误'
})
if (parseInt(res.data.code) === 401) {
clearUserInfo()
router.push('Login')
}
}
return res
},
error => {
if (parseInt(error.response.status) === 1) {
ElMessage.closeAll()
ElMessage({
type: 'error',
message: error.response.data.error ?? error.response.data.message
})
}
}
)
const post = (url, formData) => {
return new Promise((resolve, reject) => {
axios.post(url, formData).then(res => {
if (res.data.code === 0) resolve(res.data)
else reject(res.data.message)
})
})
}
export const put = (url, formData) => {
return new Promise((resolve, reject) => {
axios.put(url, formData).then(res => {
if (res.data.code === 0) resolve(res.data)
else reject(res.data.message)
})
})
}
export const get = (url, params = null) => {
if (params) {
url += '?'
for (let key in params) {
if (params[key] !== null && params[key] !== '') url += `${key}=${params[key]}&`
}
}
const lastChar = url.charAt(url.length - 1);
if (lastChar === '&' || lastChar === '?') url = url.slice(0, -1)
return new Promise((resolve, reject) => {
axios.get(url).then(res => {
if (res.data.code === 0) resolve(res.data)
else reject(res.data.message)
})
})
}
export default post

25
src/api/common.js

@ -0,0 +1,25 @@
import post from "@/api/base.js";
export const indexUrl = process.env.VUE_APP_RESOURCE_URL
//保存用户信息
export const setUserInfo = (user) => {
console.log(user)
if (user.user) window.localStorage.setItem("uid", user.user.userId);
if (user.user) window.localStorage.setItem("username", user.user.realName);
if (user.access_token) window.localStorage.setItem("token", user.access_token);
}
//获取用户信息
export const getUserInfo = () => {
return {
uid: window.localStorage.uid,
username: window.localStorage.username,
token: window.localStorage.token,
};
}
//清空用户信息
export const clearUserInfo = () => {
window.localStorage.removeItem("uid");
window.localStorage.removeItem("username");
window.localStorage.removeItem("token");
}

7
src/api/deposit.js

@ -0,0 +1,7 @@
import post, {get, SERVER_API} from "@/api/base.js";
export const pageDeposit = data => get(`think/think-deposit/page`, data)
export const addDeposit = data => post(`think/think-deposit`, data)
export const listDepositItem = data => get(`think/think-deposit-item`, data)

4
src/api/field.js

@ -0,0 +1,4 @@
import post, {get, SERVER_API} from "@/api/base.js";
export const listThinkField = data => get(`think/think-field`, data)
export const listThinkFieldWithTimeList = data => post(`think/think-field/withTimeList`, data)

10
src/api/icCard.js

@ -0,0 +1,10 @@
import post, {put, get, SERVER_API} from "@/api/base.js";
export const listThinkCard = data => get(`think/think-card`, data)
export const thinkCardInfo = id => get(`think/think-card/${id}`)
export const updateThinkCard = data => put(`think/think-card`, data)
export const pageCard = data => get(`think/think-card/page`, data)
export const addThinkCard = data => post('think/think-card', data)
export const addThinkCardLog = data => post('think/think-card-log', data)
export const thinkCardCodePay = (id, data) => post(`/think/think-card/codePay/${id}`, data)
export const thinkCardOfflinePayCheckReq = id => post(`/think/think-card/checkWechatPayRes/${id}`)

10
src/api/order.js

@ -0,0 +1,10 @@
import post, {get, put, SERVER_API} from "@/api/base.js";
export const pageOrder = data => get(`/think/think-order/page`, data)
export const getThinkOrder = id => get(`/think/think-order/${id}`)
export const addThinkOrder = data => post(`/think/think-order/add-from-device`, data)
export const thinkOrderCodePay = (id, data) => post(`/think/think-order/codePay/${id}`, data)
export const updateThinkOrder = data => put(`/think/think-order`, data)
export const offlinePayCheckReq = id => post(`/think/think-order/checkWechatPayRes/${id}`)

3
src/api/print.js

@ -0,0 +1,3 @@
import post, {get, SERVER_API} from "@/api/base.js";
export const printReq = data => post(`/print`, data)

4
src/api/site.js

@ -0,0 +1,4 @@
import post, {get, SERVER_API} from "@/api/base.js";
export const listThinkSite = data => get(`think/think-site`, data)
export const listThinkSiteTime = data => get(`think/think-site-time`, data)

3
src/api/thinkVip.js

@ -0,0 +1,3 @@
import post, {get} from "@/api/base.js";
export const listThinkVip = data => get('think/think-vip', data)

3
src/api/usersVip.js

@ -0,0 +1,3 @@
import post, {get, SERVER_API} from "@/api/base.js";
export const pageUsersVip = data => get(`think/think-users-vip/page`, data)

BIN
src/assets/bg.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 827 KiB

1
src/assets/vue.svg

@ -0,0 +1 @@
<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="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

68
src/components/ChangePwd.vue

@ -0,0 +1,68 @@
<template>
<el-dialog title="修改密码" v-model="showDialog" top="10px">
<el-form label-width="100">
<el-form-item label="原密码">
<el-input v-model="oldPwd" type="password"/>
</el-form-item>
<el-form-item label="新密码">
<el-input v-model="newPwd" type="password"/>
</el-form-item>
<el-form-item label="确认密码">
<el-input v-model="confirmPwd" type="password"/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="showDialog = false"> </el-button>
<el-button type="primary" @click="changePwd"> </el-button>
</div>
</el-dialog>
</template>
<script setup>
import {ref, watch} from 'vue'
import {ElMessage} from "element-plus";
import {changePwdReq} from "@/api/admin.js";
import {getUserInfo} from "@/api/common.js";
let showDialog = defineModel()
let oldPwd = ref(null)
let newPwd = ref(null)
let confirmPwd = ref(null)
const changePwd = async () => {
if (!oldPwd.value) {
ElMessage.error({
message: '请输入原密码'
});
return
}
if (!newPwd.value) {
ElMessage.error({
message: '请输入新密码'
});
return
}
if (!confirmPwd.value) {
ElMessage.error({
message: '请输入确认密码'
});
return
}
if (confirmPwd.value !== newPwd.value) {
ElMessage.error({
message: '新密码与确认密码不一致'
});
return
}
const res = await changePwdReq({
userId: getUserInfo().uid,
password: newPwd.value,
})
if (res) {
ElMessage.success({
message: '修改成功'
});
showDialog.value = false
}
}
</script>

136
src/components/Deposite.vue

@ -0,0 +1,136 @@
<template>
<el-dialog
v-model="dialogVisible"
title="收取押金"
width="80%"
:before-close="reload"
>
<el-form label-width="120px">
<el-form-item label="场馆">
<el-select size="large" v-model="sid" placeholder="请选择场馆" @change="getDepositItem">
<el-option
v-for="item in siteList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="抵押物品">
<el-select size="large" v-model="formData.itemId" placeholder="请选择抵押物品" @change="selectItem">
<el-option
v-for="item in depositItemList"
:key="item.value"
:label="item.title"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="抵押物品备注">
<el-input size="large" v-model="formData.remark"></el-input>
</el-form-item>
<el-form-item label="押金金额">
<el-input size="large" v-model="formData.money" disabled></el-input>
</el-form-item>
<el-form-item label="付款方式" required>
<el-select size="large" v-model="formData.moneyType" placeholder="支付方式选择" style="width: 160px;" clearable>
<el-option :value="1" label="微信支付"/>
<el-option :value="2" label="支付宝"/>
<el-option :value="3" label="现金"/>
<el-option :value="4" label="POS机"/>
</el-select>
</el-form-item>
<el-form-item label="支付凭证" required>
<el-input size="large" v-model="formData.money" disabled></el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="flex justify-center items-center">
<el-button size="large" @click="save" type="success" round style="width: 300px">收取</el-button>
<el-button size="large" @click="reload" type="danger" plain round style="width: 200px">关闭</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import {onMounted, reactive, ref} from "vue";
import {listThinkVip} from "@/api/thinkVip.js";
import {ElMessage} from "element-plus";
import {addThinkCard, addThinkCardLog, listThinkCard, pageCard} from "@/api/icCard.js";
import {reloadFlagStore} from "@/store/index.js";
import dayjs from "dayjs";
import {listThinkSite} from "@/api/site.js";
import {addDeposit, listDepositItem} from "@/api/deposit.js";
const dialogVisible = defineModel()
const defaultData = {
itemId: null,
type: 1,
money: null,
moneyType: null,
siteName: '',
name: '-',
phone: '-',
info: '',
remark: '',
isAdmin: true,
}
let formData = reactive({...defaultData})
const save = async () => {
if (formData.remark) {
formData.info = `${formData.info}(${formData.remark})`
}
if (formData.moneyType !== 1) formData.status = 1
const res = await addDeposit(formData)
if (res) {
ElMessage.success('记录成功')
formData = {...defaultData}
reload()
}
}
const sid = ref(null)
const siteList = ref([])
const getSiteList = async () => {
const res = await listThinkSite({})
if (res) {
siteList.value = res.data.map(item => {
return {
value: item.id,
label: item.name
}
})
}
}
const depositItemList = ref([])
const getDepositItem = async () => {
const site = siteList.value.find(item => item.value === sid.value)
formData.siteName = site.label
const {data} = await listDepositItem({
sid: sid.value
})
depositItemList.value = data
}
const selectItem = () => {
const item = depositItemList.value.find(item => item.id === formData.itemId)
formData.money = item.amount
}
const store = reloadFlagStore()
const reload = () => {
store.setData(true)
dialogVisible.value = false
}
onMounted(() => {
store.setData(false)
getSiteList()
})
</script>

80
src/components/IcCardInfo.vue

@ -0,0 +1,80 @@
<template>
<el-dialog
v-model="dialogVisible"
title="IC卡详情"
width="60%"
:before-close="reload"
>
<el-descriptions :column="2" v-if="cardInfo" border>
<el-descriptions-item label="卡名称">{{ cardInfo.name }}</el-descriptions-item>
<el-descriptions-item label="卡号">{{ cardInfo.code }}</el-descriptions-item>
<el-descriptions-item label="姓名">
<el-input v-model="cardInfo.username" @blur="changeField($event, 'username')"></el-input>
</el-descriptions-item>
<el-descriptions-item label="手机号">
<el-input v-model="cardInfo.phone" @blur="changeField($event, 'phone')"></el-input>
</el-descriptions-item>
<el-descriptions-item label="身份证号">{{ cardInfo.idcard }}</el-descriptions-item>
<el-descriptions-item label="过期时间">{{
dayjs(cardInfo.createTime * 1000).format('YYYY-MM-DD HH:mm:ss')
}}
</el-descriptions-item>
<el-descriptions-item label="剩余次数">{{ cardInfo.num }}</el-descriptions-item>
<el-descriptions-item label="剩余金额">{{ cardInfo.remainingMoney }}</el-descriptions-item>
<el-descriptions-item label="可用场馆">
{{
cardInfo.siteList.map(item => item.name).join()
}}
</el-descriptions-item>
</el-descriptions>
<template #footer>
<el-space>
<el-button @click="reload" size="large">关闭</el-button>
</el-space>
</template>
</el-dialog>
</template>
<script setup>
import {reloadFlagStore} from "@/store/index.js";
import {onMounted, ref} from "vue";
import dayjs from "dayjs";
import {pageCard, thinkCardInfo, updateThinkCard} from "@/api/icCard.js";
import {ElMessage} from "element-plus";
const props = defineProps({
id: Number
})
const dialogVisible = defineModel()
const cardInfo = ref(null)
const getInfo = async () => {
const {data} = await thinkCardInfo(props.id)
cardInfo.value = data
}
const changeField = async (e, field) => {
const res = await updateThinkCard({
id: props.id,
[field]: e.target.value
})
if (res && res.code === 0) ElMessage.success('修改成功')
}
onMounted(() => {
store.setData(false)
getInfo()
})
const emits = defineEmits(['close'])
const store = reloadFlagStore()
const reload = () => {
store.setData(true)
dialogVisible.value = false
emits('close')
}
</script>

296
src/components/NewCard.vue

@ -0,0 +1,296 @@
<template>
<el-dialog
v-model="dialogVisible"
title="开卡"
width="80%"
:before-close="reload"
>
<el-form label-width="120px">
<el-row :gutter="10">
<el-col :span="12">
<el-form-item label="卡名称" required>
<el-select size="large" v-model="formData.vid" placeholder="请选择VIP卡" @change="selectCard" filterable>
<el-option
v-for="item in vipList"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="VIP卡金额">
<el-input size="large" v-model="formData.price" disabled placeholder="请选择VIP卡"/>
</el-form-item>
<el-form-item label="VIP卡月限">
<el-input size="large" v-model="formData.month" disabled placeholder="请选择VIP卡">
<template #append></template>
</el-input>
</el-form-item>
<el-form-item label="VIP卡年限">
<el-input size="large" v-model="formData.term" disabled placeholder="请选择VIP卡">
<template #append></template>
</el-input>
</el-form-item>
<el-form-item label="VIP卡折扣">
<el-input size="large" v-model="formData.discount" disabled placeholder="请选择VIP卡">
<template #append></template>
</el-input>
</el-form-item>
<el-form-item label="VIP卡类型">
<el-select size="large" v-model="formData.type" placeholder="请选择VIP卡" disabled>
<el-option label="年卡" :value="1"/>
<el-option label="次卡" :value="2"/>
<el-option label="月卡" :value="3"/>
<el-option label="会员IC卡" :value="4"/>
<el-option label="充值卡" :value="5"/>
</el-select>
</el-form-item>
<el-form-item label="使用次数">
<el-input size="large" v-model="formData.count" disabled placeholder="请选择VIP卡">
<template #append></template>
</el-input>
</el-form-item>
<el-form-item label="每次减少">
<el-input size="large" v-model="formData.eachMoney" disabled placeholder="请选择VIP卡">
<template #append></template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="手机号">
<el-input size="large" v-model="formData.phone" placeholder="请输入手机号"/>
</el-form-item>
<el-form-item label="身份证号">
<el-input size="large" v-model="formData.idcard" placeholder="请输入身份证号"/>
</el-form-item>
<el-form-item label="用户姓名">
<el-input size="large" v-model="formData.username" placeholder="请输入用户姓名"/>
</el-form-item>
<el-form-item label="支付方式" required>
<el-select size="large" v-model="formData.payType" placeholder="请选择支付方式">
<el-option label="微信支付" :value="1"/>
<el-option label="支付宝支付" :value="2"/>
<el-option label="现金" :value="3"/>
<el-option label="POS机刷卡" :value="4"/>
<el-option label="平安健康卡" :value="5"/>
</el-select>
</el-form-item>
<el-form-item label="支付凭证" required v-if="formData.payType !== 1">
<el-input size="large" v-model="formData.wechatOrder" placeholder="请输入微信支付号/支付凭证号/收据单号"/>
</el-form-item>
<el-form-item label="支付密码">
<el-input size="large" v-model="formData.password" placeholder="可留空"/>
</el-form-item>
<el-form-item label="确认支付密码">
<el-input size="large" v-model="formData.confirmPassword" placeholder="可留空"/>
</el-form-item>
<el-form-item label="紧急联系人">
<el-input size="large" v-model="formData.urgentName" placeholder="请输入紧急联系人"/>
</el-form-item>
<el-form-item label="紧急联系人电话">
<el-input size="large" v-model="formData.urgentPhone" placeholder="请输入紧急联系人电话"/>
</el-form-item>
<el-form-item>
<el-button size="large" type="primary" :disabled="!formData.vid || !formData.payType" style="width: 100%"
@click="save">
确认开卡
</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div>
<el-button @click="reload">关闭</el-button>
</div>
</template>
</el-dialog>
<el-dialog v-model="showAuthCodeInput" title="扫码支付" append-to-body :close-on-click-modal="false"
@close="close">
<p class="mt-2">扫码/输入微信支付码</p>
<el-input class="mt-2" v-model="authCode" ref="authCodeInput" @input="onCodeInput" @keyup.enter.native="doPay"/>
<div slot="footer" class="text-center mt-2">
<el-space>
<el-button type="success" size="large" @click="doPay"
:disabled="!authCode">
确认支付
</el-button>
<el-button @click="showAuthCodeInput = false" size="large">关闭</el-button>
</el-space>
</div>
</el-dialog>
</template>
<script setup>
import {nextTick, onMounted, reactive, ref} from "vue";
import {listThinkVip} from "@/api/thinkVip.js";
import {ElLoading, ElMessage} from "element-plus";
import {addThinkCard, addThinkCardLog, thinkCardCodePay, thinkCardOfflinePayCheckReq} from "@/api/icCard.js";
import {reloadFlagStore} from "@/store/index.js";
import {offlinePayCheckReq, thinkOrderCodePay} from "@/api/order.js";
const dialogVisible = defineModel()
const defaultData = {
sid: null,
uid: null,
vid: null,
aid: null,
wechatOrder: null,
code: null,
name: null,
username: '',
phone: '',
price: null,
desc: null,
info: null,
discount: null,
count: null,
eachMoney: null,
remainingMoney: null,
number: null,
num: null,
status: 2,
term: null,
month: null,
type: null,
cardType: null,
vipType: null,
pic: null,
prices: null,
payType: null,
isIntegral: null,
isInvoice: null,
expireTime: null,
makeNum: null,
urgentName: null,
urgentPhone: null,
cardNum: null,
password: null,
useTime: null,
createTime: null,
idcard: null,
confirmPassword: null,
payVoucher: null,
}
const selectCard = id => {
const card = vipList.value.find(item => item.id === id)
formData.name = card.name
formData.price = card.price
formData.month = card.month
formData.term = card.term
formData.discount = card.discount
formData.type = card.type
formData.cardType = card.cardType
formData.vipType = card.vipType
formData.prices = card.prices
formData.eachMoney = card.eachMoney
formData.desc = card.desc
formData.info = card.info
formData.num = card.count
formData.count = card.count
formData.sort = card.sort
formData.sid = card.sid
formData.uid = 0
}
let formData = reactive({...defaultData})
const newCardId = ref(null)
const save = async () => {
if (formData.password && !formData.confirmPassword) {
ElMessage.error('请输入确认密码')
return
}
if (formData.password && formData.password !== formData.confirmPassword) {
ElMessage.error('两次密码不一致')
return
}
if (formData.payType !== 1 && !formData.wechatOrder) {
ElMessage.error('请输入支付凭证')
return
}
if (formData.payType !== 1) {
formData.status = 1
}
const res = await addThinkCard(formData)
if (res) {
if (formData.payType === 1) {
newCardId.value = res.data
authCode.value = ''
showAuthCodeInput.value = true
setTimeout(() => {
nextTick(() => {
authCodeInput.value.focus()
})
}, 0)
} else {
ElMessage.success('开卡成功')
formData = {...defaultData}
reload()
}
}
}
const showAuthCodeInput = ref(false)
const authCode = ref('')
const authCodeInput = ref(null)
const onCodeInput = e => {
if (e.length === 18) doPay()
}
const doPay = async () => {
const loadingInstance = ElLoading.service({fullscreen: true, text: '支付中'})
const pay_method = parseInt(authCode.value.substring(0, 2)) < 20 ? 0 : 1
if (pay_method === 1) {
ElMessage.error({message: '不支持支付宝'})
return
}
await thinkCardCodePay(newCardId.value, {code: authCode.value}).catch(() => {
loadingInstance.close()
})
await checkPayRes()
loadingInstance.close()
}
const checkPayRes = async () => {
const loadingInstance = ElLoading.service({fullscreen: true, text: '等待用户输入支付密码'})
const {data} = await thinkCardOfflinePayCheckReq(newCardId.value)
if (data.state === 'SUCCESS') {
loadingInstance.close()
ElMessage.success({message: '支付成功'})
setTimeout(() => {
reload()
}, 1000)
}else if (data.state === 'PAYERROR') {
loadingInstance.close()
ElMessage.error({message: '支付失败'})
setTimeout(() => {
reload()
}, 1000)
} else await checkPayRes()
}
const vipList = ref([])
const getVipList = async () => {
const {data} = await listThinkVip()
vipList.value = data
}
const store = reloadFlagStore()
const reload = () => {
store.setData(true)
dialogVisible.value = false
}
onMounted(() => {
store.setData(false)
getVipList()
})
</script>

349
src/components/OrderInfo.vue

@ -0,0 +1,349 @@
<template>
<el-dialog
v-model="dialogVisible"
title="订单详情"
width="60%"
:before-close="reload"
>
<el-descriptions :column="2" v-if="orderInfo">
<el-descriptions-item label="订单号">{{ orderInfo.orderNo }}</el-descriptions-item>
<el-descriptions-item label="微信订单号">{{ orderInfo.wechatOrder }}</el-descriptions-item>
<el-descriptions-item label="姓名">{{ orderInfo.name }}</el-descriptions-item>
<el-descriptions-item label="手机号">{{ orderInfo.phone }}</el-descriptions-item>
<el-descriptions-item label="场馆名称">{{ orderInfo.siteName }}</el-descriptions-item>
<el-descriptions-item label="下单时间">{{
dayjs(orderInfo.createTime * 1000).format('YYYY-MM-DD HH:mm:ss')
}}
</el-descriptions-item>
<el-descriptions-item label="订单状态">
<span v-if="orderInfo.orderStatus === 1" class="text-green-500"
>已完成</span>
<span v-if="orderInfo.orderStatus === 2" class="ele-text-placeholder"
>未使用</span>
<span v-if="orderInfo.orderStatus === 3" class="text-gray-400"
>已取消</span>
<span v-if="orderInfo.orderStatus === 4" class="ele-text-warning"
>退款申请中</span>
<span v-if="orderInfo.orderStatus === 5">退款被拒绝</span>
<span v-if="orderInfo.orderStatus === 6">退款成功</span>
<span v-if="orderInfo.orderStatus === 7">客户端申请退款</span>
<span v-if="orderInfo.orderStatus === 9">未完成</span>
</el-descriptions-item>
<el-descriptions-item label="微信昵称">{{ orderInfo.username ?? '无' }}</el-descriptions-item>
<el-descriptions-item label="优惠说明">{{ orderInfo.desc }}</el-descriptions-item>
<el-descriptions-item label="支付状态">
<span class="text-green-500" v-if="orderInfo.payStatus === 1">已支付</span>
<span class="text-red-500" v-if="orderInfo.payStatus === 2">未支付</span>
<span v-if="orderInfo.payStatus === 0 || orderInfo.payStatus === 3">未付款,占场中</span>
</el-descriptions-item>
<el-descriptions-item label="订单金额">
<span class="text-xl font-bold">{{ orderInfo.totalPrice }}</span>
</el-descriptions-item>
<el-descriptions-item label="支付方式">
<!-- <el-tag v-if="orderInfo.payType === 0">余额支付</el-tag>-->
<el-tag v-if="orderInfo.payType === 1" type="success">
微信支付
</el-tag>
<el-tag v-if="orderInfo.payType === 2">积分</el-tag>
<el-tag v-if="orderInfo.payType === 3">
支付宝
</el-tag>
<el-tag v-if="orderInfo.payType === 4">
现金
</el-tag>
<el-tag v-if="orderInfo.payType === 5">
POS机
</el-tag>
<el-tag v-if="orderInfo.payType === 6" type="warning">
VIP月卡
</el-tag>
<el-tag v-if="orderInfo.payType === 7" type="warning">
VIP年卡
</el-tag>
<el-tag v-if="orderInfo.payType === 8" type="warning">
VIP次卡
</el-tag>
<el-tag v-if="orderInfo.payType === 9" type="danger">
IC月卡
</el-tag>
<el-tag v-if="orderInfo.payType === 10" type="danger">
IC年卡
</el-tag>
<el-tag v-if="orderInfo.payType === 11" type="danger">
IC次卡
</el-tag>
<el-tag v-if="orderInfo.payType === 12">
免费
</el-tag>
<el-tag v-if="orderInfo.payType === 13" type="warning">
VIP充值卡
</el-tag>
<el-tag v-if="orderInfo.payType === 14" type="danger">
IC充值卡
</el-tag>
<el-tag v-if="orderInfo.payType === 15">
积分支付
</el-tag>
<el-tag v-if="orderInfo.payType === 16" type="warning">
VIP季卡
</el-tag>
<el-tag v-if="orderInfo.payType === 17" type="danger">
IC季卡
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="实付金额">
<span class="text-xl font-bold">{{ orderInfo.payPrice }}</span>
</el-descriptions-item>
</el-descriptions>
<el-table v-if="orderInfo" :data="orderInfo.thinkOrderInfos" style="width: 100%" stripe border
:header-cell-style="{backgroundColor: '#22C55E', color: '#FFF'}"
:cell-style="{backgroundColor: '#e2ffec'}">
<el-table-column prop="fieldName" label="场地名称" fixed width="200"/>
<el-table-column prop="price" label="成人价"/>
<el-table-column prop="childrenPrice" label="儿童价"/>
<el-table-column prop="dateTime" label="场地预约时间" width="300"/>
<el-table-column prop="adultNum" label="成人数"/>
<el-table-column prop="childrenNum" label="儿童数"/>
<el-table-column label="是否支持儿童票" v-slot="scope">
<span>{{ scope.row.isChildren === 1 ? '支持' : '不支持' }}</span>
</el-table-column>
</el-table>
<template #footer>
<el-space>
<el-button type="success" size="large" v-if="orderInfo && orderInfo.payStatus !== 1" @click="showPay = true">
立即支付
</el-button>
<el-button type="primary" size="large" @click.native="print" :disabled="disablePrint"
v-if="orderInfo && orderInfo.payStatus !== 2">打印
</el-button>
<el-button @click="reload" size="large">关闭</el-button>
</el-space>
</template>
</el-dialog>
<el-dialog
v-model="showPay"
title="订单支付"
width="50%"
>
<el-form label-width="80px">
<el-form-item label="支付方式">
<el-select v-model="formData.payType" required>
<el-option label="微信支付" :value="1"/>
<el-option label="现金" :value="4"/>
<el-option label="POS(银行卡/支付宝)" :value="5"/>
<el-option label="IC卡" :value="-1"/>
<el-option label="免费" :value="12"/>
</el-select>
</el-form-item>
<el-form-item label="IC卡选择" required v-if="formData.payType === -1">
<el-select v-model="formData.cid" placeholder="姓名/手机号/卡号搜索" filterable
:remote-method="searchCard"
@change="selectUserCard" remote>
<el-option
v-for="item in userCard"
:key="item.id"
:label="item.title"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="支付凭证" required v-if="formData.payType === 4 || formData.payType === 5">
<el-input v-model="formData.wechatOrder" placeholder="请输入支付凭证"></el-input>
</el-form-item>
</el-form>
<template #footer>
<el-space>
<el-button type="success" size="large" @click="selectPay"
:disabled="(formData.payType === -1 && !formData.cid) || ((formData.payType === 4 || formData.payType === 5) && !formData.wechatOrder)">
确认支付
</el-button>
<el-button @click="closePay" size="large">关闭</el-button>
</el-space>
</template>
</el-dialog>
<el-dialog v-model="showAuthCodeInput" title="扫码支付" append-to-body :close-on-click-modal="false"
@close="close">
<p class="mt-2">扫码/输入微信支付码</p>
<el-input class="mt-2" v-model="authCode" ref="authCodeInput" @input="onCodeInput" @keyup.enter.native="doPay"/>
<div slot="footer" class="text-center mt-2">
<el-space>
<el-button type="success" size="large" @click="doPay"
:disabled="!authCode || disabledPay">
确认支付
</el-button>
<el-button @click="showAuthCodeInput = false" size="large">关闭</el-button>
</el-space>
</div>
</el-dialog>
</template>
<script setup>
import {reloadFlagStore} from "@/store/index.js";
import {getThinkOrder, offlinePayCheckReq, thinkOrderCodePay, updateThinkOrder} from "@/api/order.js";
import {nextTick, onMounted, ref} from "vue";
import dayjs from "dayjs";
import {pageCard, updateThinkCard} from "@/api/icCard.js";
import {printReq} from "@/api/print.js";
import {ElLoading, ElMessage} from "element-plus";
const props = defineProps({
orderId: Number
})
const dialogVisible = defineModel()
const orderInfo = ref(null)
const getOrderInfo = async () => {
const {data} = await getThinkOrder(props.orderId)
console.log(data)
orderInfo.value = data
}
const showPay = ref(false)
const formData = ref({
payType: 1,
cid: null,
wechatOrder: null,
})
const userCard = ref([])
const searchCard = async (value) => {
if (!value) return
const {data} = await pageCard({
page: 1,
limit: 100,
keywords: value,
forAdmin: true
})
userCard.value = data.list.map(item => {
const expiredTime = dayjs(item.expireTime * 1000).format('YYYY-MM-DD HH:mm:ss')
let siteList = (item.siteList && item.siteList.length) ? item.siteList.map(item => item.name).join() : null
if (siteList) siteList = ` -- ${siteList}`
item.title = `有效期:${expiredTime} -- ${item.username} -- ${item.phone} -- ${item.name}${siteList}`
return item
})
}
const selectUserCard = async (id) => {
const card = userCard.value.find(item => item.id === id)
if (card) {
for (const key in formData) {
formData[key] = card[key]
}
formData.wechatOrder = null
formData.payType = null
}
}
const closePay = () => {
showPay.value = false
formData.value = {
payType: 1,
cid: null,
}
}
const showAuthCodeInput = ref(false)
const authCode = ref('')
const authCodeInput = ref(null)
const selectPay = async () => {
if (formData.value.payType === 1) {
authCode.value = ''
showAuthCodeInput.value = true
setTimeout(() => {
nextTick(() => {
authCodeInput.value.focus()
})
}, 0)
} else await changePayStatus()
}
const onCodeInput = e => {
// if (e.length === 18) doPay()
}
const disabledPay = ref(false)
const doPay = async () => {
disabledPay.value = true
const loadingInstance = ElLoading.service({fullscreen: true, text: '支付中'})
const pay_method = parseInt(authCode.value.substring(0, 2)) < 20 ? 0 : 1
if (pay_method === 1) {
ElMessage.error({message: '不支持支付宝'})
disabledPay.value = false
return
}
await thinkOrderCodePay(props.orderId, {code: authCode.value}).catch(() => {
disabledPay.value = false
loadingInstance.close()
})
await checkPayRes()
loadingInstance.close()
}
const checkPayRes = async () => {
const loadingInstance = ElLoading.service({fullscreen: true, text: '等待用户输入支付密码'})
const {data} = await offlinePayCheckReq(props.orderId)
if (data.state === 'SUCCESS') {
disabledPay.value = false
loadingInstance.close()
ElMessage.success({message: '支付成功'})
setTimeout(() => {
reload()
}, 1000)
} else if (data.state === 'PAYERROR') {
disabledPay.value = false
loadingInstance.close()
ElMessage.error({message: '支付失败'})
setTimeout(() => {
reload()
}, 1000)
} else await checkPayRes()
}
const changePayStatus = async () => {
await updateThinkOrder({
id: props.orderId,
payStatus: 1,
payTime: dayjs().unix(),
})
ElMessage.success('支付成功')
reload()
}
const disablePrint = ref(false)
const print = async () => {
disablePrint.value = true
ElLoading.service({
lock: true,
text: '正在打印...',
background: 'rgba(0, 0, 0, 0.7)'
})
const res = await printReq({
orderId: props.orderId
}).catch(() => {
disablePrint.value = false
ElLoading.service().close()
})
disablePrint.value = false
ElLoading.service().close()
console.log(res)
}
onMounted(() => {
getOrderInfo()
})
const emits = defineEmits(['close'])
const store = reloadFlagStore()
const reload = () => {
store.setData(true)
showPay.value = false
showAuthCodeInput.value = false
dialogVisible.value = false
emits('close')
}
</script>

38
src/components/Page.vue

@ -0,0 +1,38 @@
<template>
<el-pagination
class="mt-4 justify-center"
background
layout="prev, pager, next, total, sizes, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:page-size="perPage"
:page-sizes="[5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000]"
:total="total"/>
</template>
<script setup>
defineProps({
perPage: {
type: Number,
default: 50
},
total: {
type: Number,
default: 0
},
currentPage: {
type: Number,
default: 1
},
})
const emits = defineEmits(['change-size', 'change-page'])
const handleSizeChange = size =>{
emits('change-size', {size})
}
const handleCurrentChange = page =>{
emits('change-page', {page})
}
</script>

229
src/components/ReNewCard.vue

@ -0,0 +1,229 @@
<template>
<el-dialog
v-model="dialogVisible"
title="续卡"
width="80%"
:before-close="reload"
>
<el-form label-width="120px">
<el-row :gutter="10">
<el-col :span="12">
<el-form-item label="IC卡搜索">
<el-select size="large" v-model="selectedCard" placeholder="姓名/手机号/卡号搜索" filterable :remote-method="searchCard"
@change="selectUserCard" remote>
<el-option
v-for="item in userCard"
:key="item.id"
:label="item.title"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="IC卡名称">
<el-input size="large" v-model="formData.name" disabled placeholder="请选择IC卡"/>
</el-form-item>
<el-form-item label="IC卡可用场馆">
<template v-if="formData.siteList && formData.siteList.length">{{
formData.siteList.map(item => item.name).join()
}}</template>
</el-form-item>
<el-form-item label="IC卡年限">
<el-input size="large" v-model="formData.term" disabled placeholder="请选择IC卡">
<template #append></template>
</el-input>
</el-form-item>
<el-form-item label="IC卡月限">
<el-input size="large" v-model="formData.month" disabled placeholder="请选择IC卡">
<template #append></template>
</el-input>
</el-form-item>
<el-form-item label="IC卡折扣">
<el-input size="large" v-model="formData.discount" disabled placeholder="请选择IC卡">
<el-input v-model="formData.term" disabled placeholder="请选择IC卡">
<template #append></template>
</el-input>
</el-input>
</el-form-item>
<el-form-item label="IC卡类型">
<el-select size="large" v-model="formData.type" placeholder="请选择VIP卡" disabled>
<el-option label="年卡" :value="1"/>
<el-option label="次卡" :value="2"/>
<el-option label="月卡" :value="3"/>
<el-option label="会员IC卡" :value="4"/>
<el-option label="充值卡" :value="5"/>
</el-select>
</el-form-item>
<el-form-item label="使用次数">
<el-input size="large" v-model="formData.count" disabled placeholder="请选择IC卡">
<template #append></template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="手机号">
<el-input size="large" v-model="formData.phone" disabled placeholder="请选择VIP卡"/>
</el-form-item>
<el-form-item label="身份证号">
<el-input size="large" v-model="formData.idcard" disabled placeholder="请选择VIP卡"/>
</el-form-item>
<el-form-item label="用户姓名">
<el-input size="large" v-model="formData.username" disabled placeholder="请选择VIP卡"/>
</el-form-item>
<el-form-item label="支付方式" required>
<el-select size="large" v-model="formData.payType" placeholder="请选择支付方式">
<el-option label="微信支付" :value="1"/>
<el-option label="支付宝支付" :value="2"/>
<el-option label="现金" :value="3"/>
<el-option label="POS机刷卡" :value="4"/>
</el-select>
</el-form-item>
<el-form-item label="支付凭证" required>
<el-input size="large" v-model="formData.wechatOrder" placeholder="请输入微信支付号/支付凭证号/收据单号"/>
</el-form-item>
<el-form-item label="付款金额">
<span v-if="formData.price" class="text-xl text-red-500">{{ formData.price }}</span>
</el-form-item>
<el-form-item label="充值次数">
<span v-if="formData.count" class="text-xl text-red-500">{{ formData.count }}</span>
</el-form-item>
<el-form-item>
<el-button size="large" type="primary" :disabled="!formData.vid || !formData.payType" style="width: 100%" @click="save">
确认充值
</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div>
<el-button @click="reload">关闭</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import {onMounted, reactive, ref} from "vue";
import {listThinkVip} from "@/api/thinkVip.js";
import {ElMessage} from "element-plus";
import {addThinkCard, addThinkCardLog, listThinkCard, pageCard} from "@/api/icCard.js";
import {reloadFlagStore} from "@/store/index.js";
import dayjs from "dayjs";
const dialogVisible = defineModel()
const defaultData = {
sid: null,
uid: null,
vid: null,
aid: null,
wechatOrder: null,
code: null,
name: null,
username: null,
phone: null,
price: null,
desc: null,
info: null,
discount: null,
count: null,
eachMoney: null,
remainingMoney: null,
number: null,
num: null,
status: null,
term: null,
month: null,
type: null,
cardType: null,
vipType: null,
pic: null,
prices: null,
payType: null,
isIntegral: null,
isInvoice: null,
expireTime: null,
makeNum: null,
urgentName: null,
urgentPhone: null,
cardNum: null,
password: null,
useTime: null,
createTime: null,
idcard: null,
confirmPassword: null,
payVoucher: null,
siteList: [],
}
let formData = reactive({...defaultData})
const save = async () => {
if (formData.payType !== 1 && !formData.wechatOrder) {
ElMessage.error('请输入支付凭证')
return
}
const logData = {
code: formData.code,
payType: formData.payType,
money: formData.price,
wechatOrder: formData.wechatOrder,
count: formData.count,
createTime: dayjs().unix(),
}
const res = await addThinkCardLog(logData)
if (res) {
ElMessage.success('续费成功')
formData = {...defaultData}
reload()
}
}
const selectedCard = ref()
const userCard = ref([])
const searchCard = async (value) => {
if (!value) return
const {data} = await pageCard({
page:1,
limit: 100,
keywords: value,
forAdmin: true
})
userCard.value = data.list.map(item => {
const expiredTime = dayjs(item.expireTime * 1000).format('YYYY-MM-DD HH:mm:ss')
let siteList = (item.siteList && item.siteList.length) ? item.siteList.map(item => item.name).join() : null
if (siteList) siteList = ` -- ${siteList}`
item.title = `有效期:${expiredTime} -- ${item.username} -- ${item.phone} -- ${item.name}${siteList}`
return item
})
}
const selectUserCard = async (id) => {
const card = userCard.value.find(item => item.id === id)
if (card) {
for (const key in formData) {
formData[key] = card[key]
}
formData.wechatOrder = null
formData.payType = null
}
}
const vipList = ref([])
const getVipList = async () => {
const {data} = await listThinkVip()
vipList.value = data
}
const store = reloadFlagStore()
const reload = () => {
store.setData(true)
dialogVisible.value = false
}
onMounted(() => {
store.setData(false)
getVipList()
})
</script>

397
src/components/SelectSite.vue

@ -0,0 +1,397 @@
<template>
<el-dialog
v-model="dialogVisible"
:title="mode === 'select' ? '场地选择' : `场地预定 -- ${siteName}`"
width="80%"
top="10px"
:before-close="reload"
:close-on-click-modal="false"
>
<div class="flex justify-start items-start flex-wrap" v-if="mode === 'select'">
<div v-for="(item, index) in siteList"
class="w-1/4 p-2 cursor-pointer"
:key="index">
<div style="height: 100px"
@click="selectSite(item)"
class="rounded-xl flex justify-center items-center bg-gradient-to-b from-cyan-500 to-blue-500 text-white w-full h-full">
{{ item.name }}
</div>
</div>
</div>
<div v-else>
<div class="flex justify-start items-start flex-wrap">
<div v-for="(item, index) in siteList"
class="p-2 cursor-pointer"
style="flex: 0 0 15%"
:key="index">
<div style="height: 50px"
@click="selectSite(item)"
:class="[item.id === sid ? 'bg-gradient-to-b from-cyan-500 to-blue-500 text-white' : 'border']"
class="rounded-xl flex justify-center items-center w-full h-full">
{{ item.name }}
</div>
</div>
</div>
<el-divider/>
<div class="flex justify-start items-start overflow-y-auto">
<div v-for="(item, index) in dateList"
class="p-2 cursor-pointer"
style="flex: 0 0 10%"
:key="index">
<div style="height: 50px"
@click="selectDate(item)"
:class="[item === date ? 'bg-gradient-to-b from-cyan-300 to-green-500 text-white' : 'border']"
class="rounded-xl flex justify-center items-center w-full h-full">
{{ chDateStr(item) }}
</div>
</div>
</div>
<el-divider/>
<div class="flex justify-start items-start mb-2">
<div class="flex justify-center items-center" v-if="fieldList.length && fieldList[0].isHalf === 1">
<el-switch v-model="formData.type" active-color="#13ce66" inactive-color="#ff4949" active-text="全场"
:active-value="1" :inactive-value="2"
inactive-text="半场"></el-switch>
</div>
<div class="flex justify-center items-center mx-2">
<span>可预约</span>
<div style="width: 30px; height: 30px " class="border ml-2"></div>
</div>
<div class="flex justify-center items-center mx-2">
<span>选中</span>
<div style="width: 30px; height: 30px " class="border ml-2 bg-blue-400"></div>
</div>
<div class="flex justify-center items-center mx-2">
<span>已预约半场</span>
<div style="width: 30px; height: 30px " class="border ml-2 bg-green-300"></div>
</div>
<div class="flex justify-center items-center mx-2">
<span>已出售</span>
<div style="width: 30px; height: 30px " class="border ml-2 bg-red-400"></div>
</div>
<div class="flex justify-center items-center mx-2">
<span>已使用</span>
<div style="width: 30px; height: 30px " class="border ml-2 bg-gray-300"></div>
</div>
</div>
<div class=" overflow-x-auto">
<div class="flex justify-start items-start">
<div style="height: 40px; flex: 0 0 200px"
class="flex justify-center items-center w-full h-full bg-emerald-400 text-white border sticky left-0">
时间
</div>
<div v-for="(item, index) in fieldList"
style="height: 40px; flex: 0 0 200px"
class="cursor-pointer"
:key="index">
<div
class="flex justify-center items-center w-full h-full bg-emerald-400 text-white border">
{{ item.name }}
</div>
</div>
</div>
<div class="flex justify-start items-start flex-col">
<div v-for="(item, index) in periodList"
class="cursor-pointer flex justify-start items-start"
:key="index">
<div
style="height: 40px; width: 200px"
@click="selectTime(item.timePeriod)"
:class="[selectedTimeList.includes(item.timePeriod) ? 'bg-cyan-300' : '']"
class="flex justify-center items-center w-full h-full border sticky left-0 bg-white">
{{ item.timePeriod }}
</div>
<div v-for="field in fieldList"
style="height: 40px; width: 200px"
class="cursor-pointer"
:key="field.id">
<div
@click="selectField(item, field)"
:class="[(selectedTimeList.includes(item.timePeriod)) ? 'bg-cyan-50' : '',
selectedFieldTimeList.includes(`${field.id}_${item.id}`) ? 'bg-blue-400 text-white' : '',
checkSold(item, field) ? 'bg-gray-300 text-white' : '',
checkSoldButNotUsed(item, field) ? 'bg-red-400 text-white' : '',
]"
class="flex justify-center items-center w-full h-full border">
{{ item.price }}
</div>
</div>
</div>
</div>
</div>
</div>
<template #footer>
<div class="flex justify-center items-center border-t pt-2">
<el-button :disabled="!selectedFieldTimeList.length" type="success" size="large" @click="openConfirm" round
style="width: 200px" v-if="mode === 'site'">预定
</el-button>
<el-button type="danger" plain size="large" @click="reload" round style="width: 200px">关闭</el-button>
</div>
</template>
</el-dialog>
<el-dialog
v-model="showConfirm"
title="场地确认"
width="60%"
top="20px"
:close-on-click-modal="false"
>
<el-form label-width="140px">
<el-form-item label="姓名" required>
<el-input v-model="formData.name" placeholder="请输入姓名"/>
</el-form-item>
<el-form-item label="手机号码" required>
<el-input v-model="formData.phone" placeholder="请输入姓名"/>
</el-form-item>
<el-form-item label="场馆">
<el-input v-model="formData.siteName" readonly/>
</el-form-item>
<el-form-item label="合计金额">
<el-input v-model="formData.price" readonly/>
</el-form-item>
<el-form-item label="场地">
<el-space direction="vertical">
<el-button type="primary" v-for="(item, index) in selectedFieldList" :key="index">{{ item }}</el-button>
</el-space>
</el-form-item>
</el-form>
<template #footer>
<div class="flex justify-center items-center border-t pt-2">
<el-button @click.native="action(-1)" type="primary" plain size="large" round
:disabled="!formData.name || !formData.phone"
style="width: 200px">确认预定(暂不支付)
</el-button>
<el-button @click.native="action(3)" type="success" :disabled="!formData.name || !formData.phone" size="large"
round style="width: 200px">
确认预定并支付
</el-button>
</div>
</template>
</el-dialog>
<OrderInfo v-model="showInfo" v-if="showInfo" :order-id="orderId" @close="getFieldList"></OrderInfo>
</template>
<script setup>
import {onMounted, reactive, ref} from "vue";
import {listThinkSite, listThinkSiteTime} from "@/api/site.js";
import {reloadFlagStore} from "@/store/index.js";
import dayjs from "dayjs";
import {listThinkFieldWithTimeList} from "@/api/field.js";
import {ElMessage} from "element-plus";
import {addThinkOrder} from "@/api/order.js";
import OrderInfo from "@/components/OrderInfo.vue";
const siteList = ref([])
const getSiteList = async () => {
const {data} = await listThinkSite()
siteList.value = data
}
const chDateStr = (date) => {
const week = ['日', '一', '二', '三', '四', '五', '六']
return `${date} 星期${week[dayjs(date).day()]}`
}
const dateList = ref([])
const date = ref()
const week = ref()
const setDateList = async () => {
const dayNum = 180
dateList.value = []
for (let i = 0; i < dayNum; i++) {
dateList.value.push(dayjs().add(i, 'day').format('YYYY-MM-DD'))
}
selectDate(dateList.value[0])
}
const selectDate = (item) => {
date.value = item
week.value = dayjs(item).format('d') === '0' ? '7' : dayjs().format('d')
makeTimeList()
}
const dialogVisible = defineModel()
const mode = ref('select')
const sid = ref(null)
const siteName = ref('')
const selectSite = (item) => {
sid.value = item.id
siteName.value = item.name
mode.value = 'site'
getSiteTimeList()
}
const siteTimeList = ref([])
const getSiteTimeList = async () => {
const {data} = await listThinkSiteTime({
sid: sid.value,
date: date.value
})
siteTimeList.value = data
siteTimeList.value = siteTimeList.value.filter(item => {
return JSON.parse(item.week).map(Number).includes(parseInt(week.value))
})
makeTimeList()
}
const periodList = ref([])
const makeTimeList = () => {
periodList.value = []
selectedTimeList.value = []
selectedFieldTimeList.value = []
siteTimeList.value.forEach(item => {
const weekList = JSON.parse(item.week)
if (parseInt(sid.value) === 37) {
const today = dayjs().format('YYYY-MM-DD')
if (dayjs().isBefore(dayjs(today + ' 21:00:00'))) periodList.value.push(item)
} else {
if (weekList.includes(week.value)) periodList.value.push(item)
}
})
// console.log(periodList.value)
getFieldList()
}
const fidList = ref([])
const fieldList = ref([])
const getFieldList = async () => {
if (!periodList.value.length) return
const periodVoList = []
periodList.value.forEach(item => {
const timestamp = dayjs(date.value + ' ' + item.timePeriod.split(' - ')[0]).unix()
periodVoList.push({
timestamp,
period: item.timePeriod,
id: item.id
})
})
const res = await listThinkFieldWithTimeList({
sid: sid.value,
periodVoList
})
if (res) fieldList.value = res.data
console.log(fieldList.value)
console.log(periodList.value)
}
const selectedTimeList = ref([])
const selectTime = (item) => {
if (selectedTimeList.value.includes(item)) {
selectedTimeList.value = selectedTimeList.value.filter(i => i !== item)
} else {
selectedTimeList.value.push(item)
}
}
const selectedFieldTimeList = ref([])
const selectField = (time, field) => {
const period = field.periodVoList.find(item => item.id === time.id)
if (sid.value === 37) {
const today = dayjs().format('YYYY-MM-DD')
if (dayjs().isAfter(dayjs(today + ' 21:00:00'))) return ElMessage.error('已超时,无法订场');
} else {
if (dayjs().unix() > period.timestamp) return ElMessage.error('已超时,无法订场');
}
if (period.sold || period.took) return
const item = `${field.id}_${time.id}`
if (selectedFieldTimeList.value.includes(item)) {
selectedFieldTimeList.value = selectedFieldTimeList.value.filter(i => i !== item)
} else {
selectedFieldTimeList.value.push(item)
}
}
const checkSold = (time, field) => {
const item = field.periodVoList.find(item => item.id === time.id)
if (item) return item.sold
return false
}
const checkSoldButNotUsed = (time, field) => {
const item = field.periodVoList.find(item => item.id === time.id)
if (item) return item.took
return false
}
const selectedFieldList = ref([])
const showConfirm = ref(false)
const openConfirm = () => {
showConfirm.value = true
selectedFieldList.value = []
formData.price = selectedFieldTimeList.value.map(item => {
const [fid, tid] = item.split('_')
const field = fieldList.value.find(i => i.id === parseInt(fid))
const period = periodList.value.find(i => i.id === parseInt(tid))
selectedFieldList.value.push(`${date.value} ${period.timePeriod} ${field.name}`)
return period.price
}).reduce((a, b) => a + b, 0)
const site = siteList.value.find(item => item.id === parseInt(sid.value))
formData.siteName = site.name
formData.sid = site.id
}
const formData = reactive({
name: '',
phone: '',
siteName: '',
price: '',
sid: '',
type: 1,
payType: 0,
isChildren: 2,
thinkOrderInfos: []
})
const showInfo = ref(false)
const orderId = ref()
const action = async (payStatus) => {
formData.thinkOrderInfos = []
let formStartTime = ''
selectedFieldTimeList.value.forEach((item, index) => {
const [fid, tid] = item.split('_')
const field = fieldList.value.find(i => i.id === parseInt(fid))
const period = periodList.value.find(i => i.id === parseInt(tid))
const [start, end] = period.timePeriod.split(' - ')
let startTime = dayjs(`${date.value} ${start}:00`).unix()
if (index === 0) formStartTime = startTime
formData.thinkOrderInfos.push({
sid: formData.sid,
fid,
siteTimeId: tid,
siteName: formData.siteName,
fieldName: field.name,
dateTime: `${date.value}(${chDateStr(date.value).split(' ')[1]})${period.timePeriod}`,
payStatus: 3,
type: formData.type,
mergeData: `${date.value}${start}${end}${formData.sid}${fid}`,
startTime,
isChildren: formData.isChildren,
adultNum: 1,
childrenNum: 0,
})
})
formData.startTime = formStartTime
formData.payStatus = 3
const {data} = await addThinkOrder(formData)
orderId.value = data
showConfirm.value = false
showInfo.value = true
selectedFieldTimeList.value = []
selectedTimeList.value = []
}
const store = reloadFlagStore()
const reload = () => {
store.setData(true)
dialogVisible.value = false
}
onMounted(() => {
getSiteList()
setDateList()
})
</script>

11
src/config/vite.build.js

@ -0,0 +1,11 @@
const hostUrl = 'https://gxtyzx-api2.websoft.top/api'
export default {
define: {
'process.env': {
'VUE_APP_SERVER_API': 'https://gxtyzx-server.websoft.top/api',
'VUE_APP_RESOURCE_URL': hostUrl,
'VUE_APP_BASE_API': hostUrl,
}
}
}

14
src/config/vite.dev.js

@ -0,0 +1,14 @@
const hostUrl = 'https://gxtyzx-api2.websoft.top/api'
// const hostUrl = 'http://localhost:9099/api'
export default {
define: {
'process.env': {
'VUE_APP_SERVER_API': 'https://gxtyzx-server.websoft.top/api',
'VUE_APP_RESOURCE_URL': hostUrl,
'VUE_APP_BASE_API': hostUrl,
}
}
}
export {hostUrl}

25
src/main.js

@ -0,0 +1,25 @@
import {createApp} from 'vue'
import App from './App.vue'
import './style.css'
import ElementPlus from 'element-plus'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import Page from './components/Page.vue'
import { createPinia } from 'pinia'
import router from './router'
const app = createApp(App)
app.use(ElementPlus, {locale: zhCn}).use(router).component('page', Page)
const pinia = createPinia()
app.use(pinia)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.mount('#app')

118
src/pages/Deposit.vue

@ -0,0 +1,118 @@
<template>
<div class="w-full">
<div class=" m-6 rounded-xl">
<div class="p-3 w-full bg-white rounded-xl">
<el-space alignment="flex-end">
<div class="flex flex-col justify-start items-start">
<span class="text-sm text-gray-500">物品名称</span>
<el-input size="large" style="width: 300px" v-model="keywords" placeholder="物品名称筛选"
@keyup.enter.native="getList(true)"
clearable/>
</div>
<div class="flex flex-col justify-start items-start">
<span class="text-sm text-gray-500">下单时间</span>
<el-date-picker size="large" v-model="createTimeRange" type="daterange" placeholder="下单时间筛选"></el-date-picker>
</div>
<el-button size="large" icon="Search" @click.native="getList(true)" type="primary">
搜索
</el-button>
<el-button size="large" icon="RefreshRight" @click.native="init" type="warning"
margin-left="mx-4">重置
</el-button>
</el-space>
</div>
<div class="p-3 w-full rounded-xl mt-4 bg-white">
<el-table size="large" :data="list" stripe v-loading="loading" :height="tableHeight">
<el-table-column prop="money" label="押金"/>
<el-table-column prop="info" label="抵押物名称"/>
<el-table-column label="押金类型" v-slot="scope">
<span v-if="scope.row.type === 1">收取押金</span>
<span v-if="scope.row.type === 2">退押金</span>
</el-table-column>
<el-table-column prop="siteName" label="场馆名称"/>
<el-table-column label="支付方式" v-slot="scope">
<el-tag v-if="scope.row.moneyType === 1" type="success">
微信支付
</el-tag>
<el-tag v-if="scope.row.moneyType === 2">
支付宝
</el-tag>
<el-tag v-if="scope.row.moneyType === 3">
现金
</el-tag>
<el-tag v-if="scope.row.moneyType === 4">
POS机
</el-tag>
</el-table-column>
<el-table-column label="押金状态" v-slot="scope">
<span class="text-green-500" v-if="scope.row.status === 1">已付</span>
<span class="text-blue-500" v-if="scope.row.status === 2">已退</span>
<span class="text-gray-300" v-if="scope.row.status === 0">未付</span>
</el-table-column>
<el-table-column label="支付时间" v-slot="scope">
<div>{{ dayjs(scope.row.createTime * 1000).format('YYYY-MM-DD HH:mm:ss') }}</div>
</el-table-column>
</el-table>
<page :per-page="perPage" :current-page="currentPage" :total="total" @change-size="changeSize"
@change-page="changePage"/>
</div>
</div>
</div>
</template>
<script setup>
import {onMounted, ref} from 'vue'
import dayjs from "dayjs";
import {pageDeposit} from "@/api/deposit.js";
const list = ref([])
const loading = ref(true)
const perPage = ref(20)
const total = ref(0)
const currentPage = ref(1)
const changeSize = ({size}) => {
perPage.value = size
getList(true)
}
const changePage = ({page}) => {
currentPage.value = page
getList()
}
const keywords = ref('')
const payType = ref(null)
const createTimeRange = ref([])
const getList = async (reload = false) => {
let createTimeStart = null, createTimeEnd = null, orderTimeStart = null, orderTimeEnd = null
if (reload) {
currentPage.value = 1
loading.value = true
}
loading.value = true
if (createTimeRange.value && createTimeRange.value.length) {
createTimeStart = dayjs(createTimeRange.value[0]).unix()
createTimeEnd = dayjs(createTimeRange.value[1] + ' 23:59:59').unix()
}
const res = await pageDeposit({
page: currentPage.value,
limit: perPage.value,
keywords: keywords.value ?? null,
createTimeStart: createTimeStart ?? null,
createTimeEnd: createTimeEnd ?? null,
forAdmin: true
})
loading.value = false
total.value = res.data.count
list.value = res.data.list
}
const tableHeight = ref(1080)
onMounted(async () => {
tableHeight.value = document.body.clientHeight - 260
await getList()
})
</script>

142
src/pages/IcCard.vue

@ -0,0 +1,142 @@
<template>
<div class="w-full">
<div class=" m-6 rounded-xl">
<div class="p-3 w-full bg-white rounded-xl">
<el-space alignment="flex-end">
<div class="flex flex-col justify-start items-start">
<span class="text-sm text-gray-500">关键词</span>
<el-input size="large" style="width: 300px" v-model="keywords" placeholder="姓名/手机号/身份证/IC卡号筛选"
@keyup.enter.native="getList(true)"
clearable/>
</div>
<div class="flex flex-col justify-start items-start">
<span class="text-sm text-gray-500">开卡时间</span>
<el-date-picker size="large" v-model="createTimeRange" type="daterange" placeholder="开卡时间筛选"></el-date-picker>
</div>
<el-button size="large" icon="Search" @click.native="getList(true)" type="primary">
搜索
</el-button>
<el-button size="large" icon="RefreshRight" @click.native="init" type="warning"
margin-left="mx-4">重置
</el-button>
</el-space>
</div>
<div class="p-3 w-full rounded-xl mt-4 bg-white">
<el-table size="large" :data="list" stripe v-loading="loading" :height="tableHeight" @row-click="openInfo">
<el-table-column prop="id" label="IC卡号" fixed width="100"/>
<el-table-column prop="code" label="IC卡编号" width="130"/>
<el-table-column label="用户" v-slot="scope" width="130">
<div>{{ scope.row.username }}</div>
<div>{{ scope.row.phone }}</div>
</el-table-column>
<el-table-column prop="name" label="卡名称" width="160"/>
<el-table-column prop="name" label="卡类型" v-slot="scope">
<el-tag v-if="scope.row.type === 1">年卡</el-tag>
<el-tag v-if="scope.row.type === 2">次卡</el-tag>
<el-tag v-if="scope.row.type === 3">月卡</el-tag>
<el-tag v-if="scope.row.type === 4">会员IC卡</el-tag>
<el-tag v-if="scope.row.type === 5">充值卡</el-tag>
</el-table-column>
<el-table-column prop="remainingMoney" label="余额"/>
<el-table-column prop="price" label="购买价格"/>
<el-table-column prop="num" label="剩余次数"/>
<el-table-column prop="count" label="总次数"/>
<el-table-column prop="discount" label="折扣"/>
<el-table-column prop="month" label="月限"/>
<el-table-column prop="term" label="年限"/>
<el-table-column label="付款方式" v-slot="scope" width="100">
<el-tag type="success" v-if="scope.row.payType === 1">微信支付</el-tag>
<el-tag type="success" v-if="scope.row.payType === 2">支付宝支付</el-tag>
<el-tag type="success" v-if="scope.row.payType === 3">现金</el-tag>
<el-tag type="success" v-if="scope.row.payType === 4">POS机刷卡</el-tag>
<el-tag type="success" v-if="scope.row.payType === 5">平安健康卡</el-tag>
</el-table-column>
<el-table-column label="支付状态" v-slot="scope">
<span class="text-green-500" v-if="scope.row.status === 1">已支付</span>
<span class="text-red-500" v-if="scope.row.status === 2">未支付</span>
</el-table-column>
<el-table-column label="时间" v-slot="scope" width="300">
<div>开卡时间:{{ dayjs(scope.row.createTime * 1000).format('YYYY-MM-DD HH:mm:ss') }}</div>
<div class="text-blue-500">
过期时间:{{ dayjs(scope.row.expireTime * 1000).format('YYYY-MM-DD HH:mm:ss') }}
</div>
</el-table-column>
<el-table-column prop="remark" label="备注"/>
</el-table>
<page :per-page="perPage" :current-page="currentPage" :total="total" @change-size="changeSize"
@change-page="changePage"/>
</div>
</div>
</div>
<IcCardInfo v-model="showInfo" v-if="showInfo" :id="clickedId"/>
</template>
<script setup>
import {onMounted, ref, watch} from 'vue'
import dayjs from "dayjs";
import {pageCard} from "@/api/icCard.js";
import {reloadFlagStore} from "@/store/index.js";
import IcCardInfo from "@/components/IcCardInfo.vue";
const list = ref([])
const loading = ref(true)
const perPage = ref(20)
const total = ref(0)
const currentPage = ref(1)
const changeSize = ({size}) => {
perPage.value = size
getList(true)
}
const changePage = ({page}) => {
currentPage.value = page
getList()
}
const keywords = ref('')
const createTimeRange = ref([])
const getList = async (reload = false) => {
let createTimeStart = null, createTimeEnd = null, orderTimeStart = null, orderTimeEnd = null
if (reload) {
currentPage.value = 1
loading.value = true
}
loading.value = true
if (createTimeRange.value && createTimeRange.value.length) {
createTimeStart = dayjs(createTimeRange.value[0]).unix()
createTimeEnd = dayjs(createTimeRange.value[1] + ' 23:59:59').unix()
}
const res = await pageCard({
page: currentPage.value,
limit: perPage.value,
keywords: keywords.value ?? null,
createTimeStart: createTimeStart ?? null,
createTimeEnd: createTimeEnd ?? null,
forAdmin: true,
})
loading.value = false
total.value = res.data.count
list.value = res.data.list
}
const showInfo = ref(false)
const clickedId = ref()
const openInfo = row => {
clickedId.value = row.id
console.log(clickedId.value)
showInfo.value = true
}
const tableHeight = ref(1080)
onMounted(async () => {
tableHeight.value = document.body.clientHeight - 260
await getList()
})
const store = reloadFlagStore()
store.$subscribe(mutation => {
if (mutation.events.newValue) getList(true)
})
</script>

237
src/pages/Order.vue

@ -0,0 +1,237 @@
<template>
<div class="w-full">
<div class=" m-6 rounded-xl">
<div class="p-3 w-full bg-white rounded-xl">
<el-space alignment="flex-end">
<div class="flex flex-col justify-start items-start">
<span class="text-sm text-gray-500">关键词</span>
<el-input size="large" style="width: 300px" v-model="keywords" placeholder="姓名/手机号/订单号/IC卡号筛选"
@keyup.enter.native="getList(true)"
clearable/>
</div>
<div class="flex flex-col justify-start items-start">
<span class="text-sm text-gray-500">支付方式</span>
<el-select size="large" v-model="payType" placeholder="支付方式筛选" style="width: 160px;" clearable>
<el-option :value="1" label="微信支付"/>
<el-option :value="2" label="余额支付"/>
<el-option :value="3" label="支付宝"/>
<el-option :value="4" label="现金"/>
<el-option :value="5" label="POS机"/>
<el-option :value="6" label="VIP月卡"/>
<el-option :value="7" label="VIP年卡"/>
<el-option :value="8" label="VIP次卡"/>
<el-option :value="9" label="IC月卡"/>
<el-option :value="10" label="IC年卡"/>
<el-option :value="11" label="IC次卡"/>
<el-option :value="12" label="免费"/>
<el-option :value="13" label="VIP充值卡"/>
<el-option :value="14" label="IC充值卡"/>
<el-option :value="15" label="积分支付"/>
<el-option :value="16" label="VIP季卡"/>
<el-option :value="17" label="IC季卡"/>
</el-select>
</div>
<div class="flex flex-col justify-start items-start">
<span class="text-sm text-gray-500">下单时间</span>
<el-date-picker size="large" v-model="createTimeRange" type="daterange" placeholder="下单时间筛选"></el-date-picker>
</div>
<div class="flex flex-col justify-start items-start">
<span class="text-sm text-gray-500">使用场地时间</span>
<el-date-picker size="large" v-model="orderTimeRange" type="daterange" placeholder="使用场地时间筛选"></el-date-picker>
</div>
<el-button size="large" icon="Search" @click.native="getList(true)" type="primary">
搜索
</el-button>
<el-button size="large" icon="RefreshRight" @click.native="init" type="warning"
margin-left="mx-4">重置
</el-button>
</el-space>
</div>
<div class="p-3 w-full rounded-xl mt-4 bg-white">
<el-table size="large" :data="list" stripe v-loading="loading" :height="tableHeight" @row-dblclick="openInfo">
<el-table-column prop="orderNum" label="订单号" fixed width="200"/>
<el-table-column prop="title" label="场地名称" v-slot="scope" fixed width="280">
<div>{{ scope.row.siteName }}</div>
<div>{{ scope.row.dateTime }}</div>
<div>{{ scope.row.totalPrice }}</div>
</el-table-column>
<el-table-column prop="code" label="IC卡号"/>
<el-table-column label="用户" v-slot="scope">
<div>{{ scope.row.name }}</div>
<div>{{ scope.row.phone }}</div>
</el-table-column>
<el-table-column label="订单金额" v-slot="scope">
<div>{{ scope.row.totalPrice }}</div>
</el-table-column>
<el-table-column label="实付金额" v-slot="scope">
<div>{{ scope.row.payPrice }}</div>
</el-table-column>
<el-table-column label="支付方式" v-slot="scope">
<!-- <el-tag v-if="scope.row.payType === 0">余额支付</el-tag>-->
<el-tag v-if="scope.row.payType === 1" type="success">
微信支付
</el-tag>
<el-tag v-if="scope.row.payType === 2">积分</el-tag>
<el-tag v-if="scope.row.payType === 3">
支付宝
</el-tag>
<el-tag v-if="scope.row.payType === 4">
现金
</el-tag>
<el-tag v-if="scope.row.payType === 5">
POS机
</el-tag>
<el-tag v-if="scope.row.payType === 6" type="warning">
VIP月卡
</el-tag>
<el-tag v-if="scope.row.payType === 7" type="warning">
VIP年卡
</el-tag>
<el-tag v-if="scope.row.payType === 8" type="warning">
VIP次卡
</el-tag>
<el-tag v-if="scope.row.payType === 9" type="danger">
IC月卡
</el-tag>
<el-tag v-if="scope.row.payType === 10" type="danger">
IC年卡
</el-tag>
<el-tag v-if="scope.row.payType === 11" type="danger">
IC次卡
</el-tag>
<el-tag v-if="scope.row.payType === 12">
免费
</el-tag>
<el-tag v-if="scope.row.payType === 13" type="warning">
VIP充值卡
</el-tag>
<el-tag v-if="scope.row.payType === 14" type="danger">
IC充值卡
</el-tag>
<el-tag v-if="scope.row.payType === 15">
积分支付
</el-tag>
<el-tag v-if="scope.row.payType === 16" type="warning">
VIP季卡
</el-tag>
<el-tag v-if="scope.row.payType === 17" type="danger">
IC季卡
</el-tag>
</el-table-column>
<el-table-column label="支付状态" v-slot="scope">
<span class="text-green-500" v-if="scope.row.payStatus === 1">已支付</span>
<span class="text-red-500" v-if="scope.row.payStatus === 2">未支付</span>
<span v-if="scope.row.payStatus === 0 || scope.row.payStatus === 3">未付款,占场中</span>
</el-table-column>
<el-table-column label="订单状态" v-slot="scope">
<span v-if="scope.row.orderStatus === 1" class="text-green-500"
>已完成</span>
<span v-if="scope.row.orderStatus === 2" class="ele-text-placeholder"
>未使用</span>
<span v-if="scope.row.orderStatus === 3" class="text-gray-400"
>已取消</span>
<span v-if="scope.row.orderStatus === 4" class="ele-text-warning"
>退款申请中</span>
<span v-if="scope.row.orderStatus === 5">退款被拒绝</span>
<span v-if="scope.row.orderStatus === 6">退款成功</span>
<span v-if="scope.row.orderStatus === 7">客户端申请退款</span>
<span v-if="scope.row.orderStatus === 9">未完成</span>
</el-table-column>
<el-table-column label="时间" v-slot="scope" width="300">
<div>下单时间:{{ dayjs(scope.row.createTime * 1000).format('YYYY-MM-DD HH:mm:ss') }}</div>
<div class="text-green-500">更新时间:{{
dayjs(scope.row.updateTime * 1000).format('YYYY-MM-DD HH:mm:ss')
}}
</div>
<div class="text-blue-500" v-if="scope.row.payTime">
支付时间:{{ dayjs(scope.row.payTime * 1000).format('YYYY-MM-DD HH:mm:ss') }}
</div>
</el-table-column>
<el-table-column label="操作">
<template v-slot="scope">
<el-button type="text" @click="openInfo(scope.row)">详情</el-button>
</template>
</el-table-column>
</el-table>
<page :per-page="perPage" :current-page="currentPage" :total="total" @change-size="changeSize"
@change-page="changePage"/>
</div>
</div>
<OrderInfo v-model="showInfo" v-if="showInfo" :order-id="orderId" @close="getList(true)"></OrderInfo>
</div>
</template>
<script setup>
import {onMounted, ref} from 'vue'
import {pageOrder} from "@/api/order.js";
import dayjs from "dayjs";
import OrderInfo from "@/components/OrderInfo.vue";
const list = ref([])
const loading = ref(true)
const perPage = ref(20)
const total = ref(0)
const currentPage = ref(1)
const changeSize = ({size}) => {
perPage.value = size
getList(true)
}
const changePage = ({page}) => {
currentPage.value = page
getList()
}
const keywords = ref('')
const payType = ref(null)
const createTimeRange = ref([])
const orderTimeRange = ref([])
const getList = async (reload = false) => {
let createTimeStart = null, createTimeEnd = null, orderTimeStart = null, orderTimeEnd = null
if (reload) {
currentPage.value = 1
loading.value = true
}
loading.value = true
if (createTimeRange.value && createTimeRange.value.length) {
createTimeStart = dayjs(createTimeRange.value[0]).unix()
createTimeEnd = dayjs(createTimeRange.value[1] + ' 23:59:59').unix()
}
console.log(orderTimeRange.value)
console.log(dayjs(orderTimeRange.value[1] + ' 23:59:59').unix())
if (orderTimeRange.value && orderTimeRange.value.length) {
orderTimeStart = dayjs(orderTimeRange.value[0]).unix()
orderTimeEnd = dayjs(orderTimeRange.value[1] + ' 23:59:59').unix()
}
const res = await pageOrder({
page: currentPage.value,
limit: perPage.value,
keywords: keywords.value ?? null,
payType: payType.value ?? null,
createTimeStart: createTimeStart ?? null,
createTimeEnd: createTimeEnd ?? null,
orderTimeStart: orderTimeStart ?? null,
orderTimeEnd: orderTimeEnd ?? null,
forAdmin: true
})
loading.value = false
total.value = res.data.count
list.value = res.data.list
}
const showInfo = ref(false)
const orderId = ref()
const openInfo = (row) => {
console.log(row)
orderId.value = row.id
showInfo.value = true
}
const tableHeight = ref(1080)
onMounted(async () => {
tableHeight.value = document.body.clientHeight - 260
await getList()
})
</script>

130
src/pages/ReNewIcCard.vue

@ -0,0 +1,130 @@
<template>
<div class="w-full">
<div class=" m-6 rounded-xl">
<div class="p-3 w-full bg-white rounded-xl">
<el-space alignment="flex-end">
<div class="flex flex-col justify-start items-start">
<span class="text-sm text-gray-500">关键词</span>
<el-input size="large" style="width: 300px" v-model="keywords" placeholder="姓名/手机号/身份证/IC卡号筛选"
@keyup.enter.native="getList(true)"
clearable/>
</div>
<div class="flex flex-col justify-start items-start">
<span class="text-sm text-gray-500">开卡时间</span>
<el-date-picker size="large" v-model="createTimeRange" type="daterange" placeholder="开卡时间筛选"></el-date-picker>
</div>
<el-button size="large" icon="Search" @click.native="getList(true)" type="primary">
搜索
</el-button>
<el-button size="large" icon="RefreshRight" @click.native="init" type="warning"
margin-left="mx-4">重置
</el-button>
</el-space>
</div>
<div class="p-3 w-full rounded-xl mt-4 bg-white">
<el-table :data="list" stripe v-loading="loading" :height="tableHeight" size="large">
<el-table-column prop="id" label="IC卡号"/>
<el-table-column prop="code" label="IC卡编号"/>
<el-table-column label="用户" v-slot="scope">
<div>{{ scope.row.username }}</div>
<div>{{ scope.row.phone }}</div>
</el-table-column>
<el-table-column prop="name" label="卡名称"/>
<el-table-column prop="number" label="续卡次数"/>
<el-table-column prop="name" label="卡类型" v-slot="scope">
<el-tag v-if="scope.row.type === 1">年卡</el-tag>
<el-tag v-if="scope.row.type === 2">次卡</el-tag>
<el-tag v-if="scope.row.type === 3">月卡</el-tag>
<el-tag v-if="scope.row.type === 4">会员IC卡</el-tag>
<el-tag v-if="scope.row.type === 5">充值卡</el-tag>
</el-table-column>
<el-table-column prop="remainingMoney" label="余额"/>
<el-table-column prop="price" label="购买价格"/>
<el-table-column label="可用场馆" v-slot="scope">
<span>{{ scope.row.siteList.map(item => item.name).join() }}</span>
</el-table-column>
<el-table-column prop="num" label="剩余次数"/>
<el-table-column prop="count" label="总次数"/>
<el-table-column prop="discount" label="折扣"/>
<el-table-column prop="month" label="月限"/>
<el-table-column prop="term" label="年限"/>
<el-table-column label="付款方式" v-slot="scope">
<el-tag type="success" v-if="scope.row.payType === 1">微信支付</el-tag>
<el-tag type="success" v-if="scope.row.payType === 2">支付宝支付</el-tag>
<el-tag type="success" v-if="scope.row.payType === 3">现金</el-tag>
<el-tag type="success" v-if="scope.row.payType === 4">POS机刷卡</el-tag>
<el-tag type="success" v-if="scope.row.payType === 5">平安健康卡</el-tag>
</el-table-column>
<el-table-column label="支付状态" v-slot="scope">
<span class="text-green-500" v-if="scope.row.status === 1">已支付</span>
<span class="text-red-500" v-if="scope.row.status === 2">未支付</span>
</el-table-column>
<el-table-column label="时间" v-slot="scope">
<div>开卡时间:{{ dayjs(scope.row.createTime * 1000).format('YYYY-MM-DD HH:mm:ss') }}</div>
<div class="text-blue-500">
过期时间:{{ dayjs(scope.row.expireTime * 1000).format('YYYY-MM-DD HH:mm:ss') }}
</div>
</el-table-column>
<el-table-column prop="remark" label="备注"/>
</el-table>
<page :per-page="perPage" :current-page="currentPage" :total="total" @change-size="changeSize"
@change-page="changePage"/>
</div>
</div>
</div>
</template>
<script setup>
import {onMounted, ref} from 'vue'
import dayjs from "dayjs";
import {pageCard} from "@/api/icCard.js";
const list = ref([])
const loading = ref(true)
const perPage = ref(20)
const total = ref(0)
const currentPage = ref(1)
const changeSize = ({size}) => {
perPage.value = size
getList(true)
}
const changePage = ({page}) => {
currentPage.value = page
getList()
}
const keywords = ref('')
const createTimeRange = ref([])
const getList = async (reload = false) => {
let createTimeStart = null, createTimeEnd = null, orderTimeStart = null, orderTimeEnd = null
if (reload) {
currentPage.value = 1
loading.value = true
}
loading.value = true
if (createTimeRange.value && createTimeRange.value.length) {
createTimeStart = dayjs(createTimeRange.value[0]).unix()
createTimeEnd = dayjs(createTimeRange.value[1] + ' 23:59:59').unix()
}
const res = await pageCard({
page: currentPage.value,
limit: perPage.value,
keywords: keywords.value ?? null,
createTimeStart: createTimeStart ?? null,
createTimeEnd: createTimeEnd ?? null,
notZeroNumber: true,
})
loading.value = false
total.value = res.data.count
list.value = res.data.list
}
const tableHeight = ref(1080)
onMounted(async () => {
tableHeight.value = document.body.clientHeight - 260
await getList()
})
</script>

126
src/pages/UsersVip.vue

@ -0,0 +1,126 @@
<template>
<div class="w-full">
<div class=" m-6 rounded-xl">
<div class="p-3 w-full bg-white rounded-xl">
<el-space alignment="flex-end">
<div class="flex flex-col justify-start items-start">
<span class="text-sm text-gray-500">关键词</span>
<el-input size="large" style="width: 300px" v-model="keywords" placeholder="姓名/手机号/身份证/IC卡号筛选"
@keyup.enter.native="getList(true)"
clearable/>
</div>
<div class="flex flex-col justify-start items-start">
<span class="text-sm text-gray-500">开卡时间</span>
<el-date-picker size="large" v-model="createTimeRange" type="daterange" placeholder="开卡时间筛选"></el-date-picker>
</div>
<el-button size="large" icon="Search" @click.native="getList(true)" type="primary">
搜索
</el-button>
<el-button size="large" icon="RefreshRight" @click.native="init" type="warning"
margin-left="mx-4">重置
</el-button>
</el-space>
</div>
<div class="p-3 w-full rounded-xl mt-4 bg-white">
<el-table :data="list" stripe v-loading="loading" :height="tableHeight" size="large">
<el-table-column prop="id" label="IC卡号"/>
<el-table-column prop="code" label="IC卡编号"/>
<el-table-column label="用户" v-slot="scope">
<div>{{ scope.row.username }}</div>
<div>{{ scope.row.phone }}</div>
</el-table-column>
<el-table-column prop="name" label="卡名称"/>
<el-table-column prop="name" label="卡类型" v-slot="scope">
<el-tag v-if="scope.row.type === 1">年卡</el-tag>
<el-tag v-if="scope.row.type === 2">次卡</el-tag>
<el-tag v-if="scope.row.type === 3">月卡</el-tag>
<el-tag v-if="scope.row.type === 4">会员IC卡</el-tag>
<el-tag v-if="scope.row.type === 5">充值卡</el-tag>
</el-table-column>
<el-table-column prop="remainingMoney" label="余额"/>
<el-table-column prop="price" label="购买价格"/>
<el-table-column prop="siteNames" label="可用场馆"/>
<el-table-column prop="num" label="剩余次数"/>
<el-table-column prop="count" label="总次数"/>
<el-table-column prop="discount" label="折扣"/>
<el-table-column prop="month" label="月限"/>
<el-table-column prop="term" label="年限"/>
<el-table-column label="支付状态" v-slot="scope">
<span class="text-green-500" v-if="scope.row.status === 1">已支付</span>
<span class="text-red-500" v-if="scope.row.status === 2">未支付</span>
<span class="text-red-500" v-if="scope.row.status === 3">已退款</span>
</el-table-column>
<el-table-column label="是否已开具发票" v-slot="scope">
<span class="text-green-500" v-if="scope.row.isInvoice === 1">已开</span>
<span class="text-red-500" v-if="scope.row.isInvoice === 2">未开</span>
<span class="text-gray-500" v-if="scope.row.isInvoice === 3">不可未开</span>
</el-table-column>
<el-table-column label="时间" v-slot="scope">
<div>开卡时间:{{ dayjs(scope.row.createTime * 1000).format('YYYY-MM-DD HH:mm:ss') }}</div>
<div class="text-blue-500">
过期时间:{{ dayjs(scope.row.expireTime * 1000).format('YYYY-MM-DD HH:mm:ss') }}
</div>
</el-table-column>
<el-table-column prop="remark" label="备注"/>
</el-table>
<page :per-page="perPage" :current-page="currentPage" :total="total" @change-size="changeSize"
@change-page="changePage"/>
</div>
</div>
</div>
</template>
<script setup>
import {onMounted, ref} from 'vue'
import dayjs from "dayjs";
import {pageUsersVip} from "@/api/usersVip.js";
const list = ref([])
const loading = ref(true)
const perPage = ref(20)
const total = ref(0)
const currentPage = ref(1)
const changeSize = ({size}) => {
perPage.value = size
getList(true)
}
const changePage = ({page}) => {
currentPage.value = page
getList()
}
const keywords = ref('')
const createTimeRange = ref([])
const getList = async (reload = false) => {
let createTimeStart = null, createTimeEnd = null
if (reload) {
currentPage.value = 1
loading.value = true
}
loading.value = true
if (createTimeRange.value && createTimeRange.value.length) {
createTimeStart = dayjs(createTimeRange.value[0]).unix()
createTimeEnd = dayjs(createTimeRange.value[1] + ' 23:59:59').unix()
}
const res = await pageUsersVip({
page: currentPage.value,
limit: perPage.value,
keywords: keywords.value ?? null,
createTimeStart: createTimeStart ?? null,
createTimeEnd: createTimeEnd ?? null,
forAdmin: true,
})
loading.value = false
total.value = res.data.count
list.value = res.data.list
}
const tableHeight = ref(1080)
onMounted(async () => {
tableHeight.value = document.body.clientHeight - 260
await getList()
})
</script>

163
src/pages/VipCardLog.vue

@ -0,0 +1,163 @@
<template>
<div class="w-full">
<div class=" m-6 rounded-xl">
<div class="p-3 w-full bg-white rounded-xl">
<el-space alignment="flex-end">
<div class="flex flex-col justify-start items-start">
<span class="text-sm text-gray-500">关键词</span>
<el-input style="width: 300px" v-model="keywords" placeholder="姓名/手机号/订单号/IC卡号筛选"
@keyup.enter.native="getList(true)"
clearable/>
</div>
<div class="flex flex-col justify-start items-start">
<span class="text-sm text-gray-500">下单时间</span>
<el-date-picker v-model="createTimeRange" type="daterange" placeholder="下单时间筛选"></el-date-picker>
</div>
<div class="flex flex-col justify-start items-start">
<span class="text-sm text-gray-500">使用场地时间</span>
<el-date-picker v-model="orderTimeRange" type="daterange" placeholder="使用场地时间筛选"></el-date-picker>
</div>
<el-button icon="Search" @click.native="getList(true)" type="primary">
搜索
</el-button>
<el-button icon="RefreshRight" @click.native="init" type="warning"
margin-left="mx-4">重置
</el-button>
</el-space>
</div>
<div class="p-3 w-full rounded-xl mt-4 bg-white">
<el-table :data="list" stripe v-loading="loading" :height="tableHeight" @row-dblclick="openInfo">
<el-table-column prop="orderNum" label="订单号"/>
<el-table-column prop="title" label="场地名称" v-slot="scope">
<div>{{ scope.row.siteName }}</div>
<div>{{ scope.row.dateTime }} {{ scope.row.totalPrice }}</div>
</el-table-column>
<el-table-column label="VIP卡" v-slot="scope">
<p>{{ scope.row.thinkVip.name }}</p>
<p>{{ scope.row.thinkVip.code }}</p>
</el-table-column>
<el-table-column label="用户" v-slot="scope">
<div>{{ scope.row.name }}</div>
<div>{{ scope.row.phone }}</div>
</el-table-column>
<el-table-column label="订单金额" v-slot="scope">
<div>{{ scope.row.totalPrice }}</div>
</el-table-column>
<el-table-column label="实付金额" v-slot="scope">
<div>{{ scope.row.payPrice }}</div>
</el-table-column>
<el-table-column label="支付状态" v-slot="scope">
<span class="text-green-500" v-if="scope.row.payStatus === 1">已支付</span>
<span class="text-red-500" v-if="scope.row.payStatus === 2">未支付</span>
<span v-if="scope.row.payStatus === 0 || scope.row.payStatus === 3">未付款,占场中</span>
</el-table-column>
<el-table-column label="订单状态" v-slot="scope">
<span v-if="scope.row.orderStatus === 1" class="text-green-500"
>已完成</span>
<span v-if="scope.row.orderStatus === 2" class="ele-text-placeholder"
>未使用</span>
<span v-if="scope.row.orderStatus === 3" class="text-gray-400"
>已取消</span>
<span v-if="scope.row.orderStatus === 4" class="ele-text-warning"
>退款申请中</span>
<span v-if="scope.row.orderStatus === 5">退款被拒绝</span>
<span v-if="scope.row.orderStatus === 6">退款成功</span>
<span v-if="scope.row.orderStatus === 7">客户端申请退款</span>
<span v-if="scope.row.orderStatus === 9">未完成</span>
</el-table-column>
<el-table-column label="时间" v-slot="scope">
<div>下单时间:{{ dayjs(scope.row.createTime * 1000).format('YYYY-MM-DD HH:mm:ss') }}</div>
<div class="text-green-500">更新时间:{{
dayjs(scope.row.updateTime * 1000).format('YYYY-MM-DD HH:mm:ss')
}}
</div>
<div class="text-blue-500" v-if="scope.row.payTime">
支付时间:{{ dayjs(scope.row.payTime * 1000).format('YYYY-MM-DD HH:mm:ss') }}
</div>
</el-table-column>
<el-table-column label="操作">
<template v-slot="scope">
<el-button type="text" @click="openInfo(scope.row)">详情</el-button>
</template>
</el-table-column>
</el-table>
<page :per-page="perPage" :current-page="currentPage" :total="total" @change-size="changeSize"
@change-page="changePage"/>
</div>
</div>
<OrderInfo v-model="showInfo" v-if="showInfo" :order-id="orderId"></OrderInfo>
</div>
</template>
<script setup>
import {onMounted, ref} from 'vue'
import {pageOrder} from "@/api/order.js";
import dayjs from "dayjs";
import OrderInfo from "@/components/OrderInfo.vue";
const list = ref([])
const loading = ref(true)
const perPage = ref(20)
const total = ref(0)
const currentPage = ref(1)
const changeSize = ({size}) => {
perPage.value = size
getList(true)
}
const changePage = ({page}) => {
currentPage.value = page
getList()
}
const keywords = ref('')
const payType = ref(null)
const createTimeRange = ref([])
const orderTimeRange = ref([])
const getList = async (reload = false) => {
let createTimeStart = null, createTimeEnd = null, orderTimeStart = null, orderTimeEnd = null
if (reload) {
currentPage.value = 1
loading.value = true
}
loading.value = true
if (createTimeRange.value && createTimeRange.value.length) {
createTimeStart = dayjs(createTimeRange.value[0]).unix()
createTimeEnd = dayjs(createTimeRange.value[1] + ' 23:59:59').unix()
}
if (orderTimeRange.value && orderTimeRange.value.length) {
orderTimeStart = dayjs(orderTimeRange.value[0]).unix()
orderTimeEnd = dayjs(orderTimeRange.value[1] + ' 23:59:59').unix()
}
const res = await pageOrder({
page: currentPage.value,
limit: perPage.value,
keywords: keywords.value ?? null,
createTimeStart: createTimeStart ?? null,
createTimeEnd: createTimeEnd ?? null,
orderTimeStart: orderTimeStart ?? null,
orderTimeEnd: orderTimeEnd ?? null,
forAdmin: true,
useVip: true,
})
loading.value = false
total.value = res.data.count
list.value = res.data.list
}
const showInfo = ref(false)
const orderId = ref()
const openInfo = (row) => {
console.log(row)
orderId.value = row.id
showInfo.value = true
}
const tableHeight = ref(1080)
onMounted(async () => {
tableHeight.value = document.body.clientHeight - 260
await getList()
})
</script>

59
src/pages/index/Index.vue

@ -0,0 +1,59 @@
<template>
<div class="flex justify-center items-center flex-col" style="height: calc(100vh - 80px)">
<div class="flex justify-center items-center w-1/3">
<div class="flex justify-center items-center rounded-xl item-bg m-4 w-1/3" @click="showNewCard = true">
开卡
</div>
<div class="flex justify-center items-center rounded-xl item-bg m-4 w-1/3" @click="showReNewCard = true">
续卡
</div>
<div class="flex justify-center items-center rounded-xl item-bg m-4 w-1/3" @click="showSiteList = true">
预定场地
</div>
</div>
<div class="flex justify-center items-center w-1/3">
<div class="flex justify-center items-center rounded-xl item-bg m-4 w-1/2" @click="showDeposit = true">
收取押金
</div>
<div class="flex justify-center items-center rounded-xl item-bg m-4 w-1/2" @click="deving">
我的报表
</div>
<!-- <div class="flex justify-center items-center rounded-xl item-bg m-4 w-1/3">-->
<!-- 转卡服务-->
<!-- </div>-->
</div>
</div>
<NewCard v-model="showNewCard" v-if="showNewCard"></NewCard>
<ReNewCard v-model="showReNewCard" v-if="showReNewCard"></ReNewCard>
<SelectSite v-model="showSiteList" v-if="showSiteList"></SelectSite>
<Deposite v-model="showDeposit" v-if="showDeposit"></Deposite>
</template>
<script setup>
import {onMounted, reactive, ref} from 'vue'
import NewCard from "@/components/NewCard.vue";
import ReNewCard from "@/components/ReNewCard.vue";
import SelectSite from "@/components/SelectSite.vue";
import Deposite from "@/components/Deposite.vue";
import {ElMessage} from "element-plus";
const showNewCard = ref(false)
const showReNewCard = ref(false)
const showSiteList = ref(false)
const showDeposit = ref(false)
const deving = () => {
ElMessage.info('开发中,暂未开放')
}
</script>
<style scope>
.item-bg {
background: linear-gradient(to bottom, #27ABAB, #27AB49);
height: 10rem;
flex-shrink: 0;
color: white;
cursor: pointer;
font-size: 1.25rem;
}
</style>

17
src/router/index.js

@ -0,0 +1,17 @@
import * as VueRouter from "vue-router";
import Home from '@/views/Home.vue'
const routes = [
{
path: '/:page',
name: 'home',
component: Home
}
]
const router = VueRouter.createRouter({
history: VueRouter.createWebHashHistory(),
routes,
})
export default router

10
src/store/index.js

@ -0,0 +1,10 @@
import {defineStore} from 'pinia'
import {computed, ref} from "vue";
export const reloadFlagStore = defineStore('reloadFlag', () => {
const data = ref(null)
const getData = computed(() => data.value)
const setData = (value) => data.value = value
return {getData, setData, data}
})

10
src/style.css

@ -0,0 +1,10 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.card {
@apply shadow-md rounded-lg p-4 bg-white;
}
}

205
src/views/Home.vue

@ -0,0 +1,205 @@
<template>
<el-container class="w-screen h-screen relative">
<div class="w-screen h-screen absolute left-0 top-0 bg"/>
<el-header class="z-10">
<div class="flex justify-between items-center bg-white w-full px-4 rounded-full mt-2">
<el-space>
<div class="flex flex-col justify-start items-start z-10 text-sm">
<span>欢迎您, {{ username }}</span>
<span class="mt-2">{{ time }}</span>
</div>
<div class="ml-4 flex justify-start items-center" v-if="activeIndex !== 'Index'">
<div
class="cursor-pointer flex flex-col justify-center items-center p-1 rounded my-0.5 mx-2 bg-emerald-400 text-white"
style="width: 100px" @click="showNewCard = true">
<el-icon size="32">
<CreditCard></CreditCard>
</el-icon>
<span class="text-sm">开卡</span>
</div>
<div
class="cursor-pointer flex flex-col justify-center items-center p-1 rounded my-0.5 mx-2 bg-emerald-400 text-white"
style="width: 100px" @click="showReNewCard = true">
<el-icon size="32">
<Money></Money>
</el-icon>
<span class="text-sm">续卡</span>
</div>
<div
class="cursor-pointer flex flex-col justify-center items-center p-1 rounded my-0.5 mx-2 bg-emerald-400 text-white"
style="width: 100px" @click="showSiteList = true">
<el-icon size="32">
<Football></Football>
</el-icon>
<span class="text-sm">预定场地</span>
</div>
<div
class="cursor-pointer flex flex-col justify-center items-center p-1 rounded my-0.5 mx-2 bg-emerald-400 text-white"
style="width: 100px" @click="showDeposit = true">
<el-icon size="32">
<Sell></Sell>
</el-icon>
<span class="text-sm">收取押金</span>
</div>
<!-- <el-button round plain type="primary" @click="showNewCard = true">开卡</el-button>-->
<!-- <el-button round plain type="primary" @click="showReNewCard = true">续卡</el-button>-->
<!-- <el-button round plain type="primary" @click="showSiteList = true">预定场地</el-button>-->
<!-- <el-button round plain type="success" @click="showDeposit = true">收取押金</el-button>-->
<!-- <el-button round plain type="success">我的报表</el-button>-->
<!-- <el-button round plain type="success">转卡服务</el-button>-->
</div>
</el-space>
<div class="rounded-full pr-10">
<el-menu
:default-active="activeIndex"
class="el-menu-demo"
mode="horizontal"
:ellipsis="false"
@select="selectMenu"
active-text-color="#27AB49"
>
<el-menu-item index="Index">主页</el-menu-item>
<el-menu-item index="IcCard">卡号列表</el-menu-item>
<el-menu-item index="ReNewIcCard">续卡列表</el-menu-item>
<el-menu-item index="Order">订单列表</el-menu-item>
<el-menu-item index="UsersVip">VIP购卡记录列表</el-menu-item>
<!-- <el-sub-menu index="4">-->
<!-- <template #title>电子卡列表</template>-->
<!-- <el-menu-item index="UsersVip">VIP购卡记录列表</el-menu-item>-->
<!-- <el-menu-item index="VipCardLog">VIP卡消费流水</el-menu-item>-->
<!-- </el-sub-menu>-->
<el-menu-item index="Deposit">押金列表</el-menu-item>
<el-menu-item index="ChangePwd">修改密码</el-menu-item>
<el-menu-item index="Logout">
<el-button type="danger" size="large" round>退出登录</el-button>
</el-menu-item>
<el-menu-item>
<span class="text-sm">当前版本:{{ version }}</span>
</el-menu-item>
</el-menu>
</div>
</div>
</el-header>
<el-main class="flex flex-col z-20" style="padding: 0">
<keep-alive>
<component ref="ComponentRef" :is="activeIndex" :keep-alive="true" class="w-full"/>
</keep-alive>
</el-main>
</el-container>
<NewCard v-model="showNewCard" v-if="showNewCard"></NewCard>
<ReNewCard v-model="showReNewCard" v-if="showReNewCard"></ReNewCard>
<SelectSite v-model="showSiteList" v-if="showSiteList"></SelectSite>
<ChangePwd v-model="showChangePwd" v-if="showChangePwd"></ChangePwd>
<Deposite v-model="showDeposit" v-if="showDeposit"></Deposite>
</template>
<script>
import Index from '@/pages/index/Index.vue'
import Order from '@/pages/Order.vue'
import IcCard from '@/pages/IcCard.vue'
import ReNewIcCard from '@/pages/ReNewIcCard.vue'
import Deposit from '@/pages/Deposit.vue'
import UsersVip from '@/pages/UsersVip.vue'
import VipCardLog from '@/pages/VipCardLog.vue'
export default {
components: {
Index,
Order,
IcCard,
ReNewIcCard,
Deposit,
UsersVip,
VipCardLog,
}
}
</script>
<script setup>
import {ref, watch, onMounted, nextTick, reactive} from 'vue'
import {clearUserInfo, getUserInfo} from '@/api/common.js'
import {useRoute, useRouter} from 'vue-router'
import {ElMessageBox} from "element-plus";
import dayjs from "dayjs";
import NewCard from "@/components/NewCard.vue";
import ReNewCard from "@/components/ReNewCard.vue";
import SelectSite from "@/components/SelectSite.vue";
import ChangePwd from "@/components/ChangePwd.vue";
import Deposite from "@/components/Deposite.vue";
const version = ref('250119')
const route = useRoute()
const router = useRouter()
const currentView = ref(null)
const activeIndex = ref('Index')
const username = getUserInfo().username
const time = ref()
const setCurrentTime = () => {
time.value = dayjs().format('YYYY-MM-DD HH:mm:ss')
setTimeout(() => {
setCurrentTime()
}, 1000)
}
setCurrentTime()
watch(route, (path) => {
currentView.value = path.path
activeIndex.value = currentView.value.split('/')[1]
console.log(activeIndex.value)
// getDotData()
})
const showChangePwd = ref(false)
const selectMenu = menu => {
switch (menu) {
case 'Logout' : {
ElMessageBox.confirm('您确定要退出登录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
clearUserInfo()
router.push(`/Login`)
}).catch(() => {
// console.log('');
});
}
break;
case 'ChangePwd' : {
showChangePwd.value = true
}
break;
default : {
router.push(`/${menu}`)
}
}
}
const showNewCard = ref(false)
const showReNewCard = ref(false)
const showSiteList = ref(false)
const showDeposit = ref(false)
onMounted(async () => {
if (!getUserInfo().token) {
await clearUserInfo()
await router.push(`/Login`)
}
const urlArr = window.location.href.split('/')
activeIndex.value = urlArr[urlArr.length - 1]
if (!activeIndex.value) await router.push(`/Index`)
})
</script>
<style scoped>
.bg {
z-index: 0;
background: linear-gradient(to bottom, #27ABAB44, #27AB4944), url("@/assets/bg.jpg") no-repeat 100% 100%;
opacity: .9;
}
</style>

57
src/views/Login.vue

@ -0,0 +1,57 @@
<template>
<div class="flex w-screen h-screen justify-center items-center bg ">
<div class="w-1/4 flex justify-center items-center rounded-xl bg-white">
<!-- <img src="@/assets/images/login-icon.png" alt="" class="mr-1 w-2/4">-->
<div class="flex justify-center items-center flex-col w-full p-4 shadow-xl rounded-xl">
<!-- <img src="@/assets/images/logo.jpg" alt="" style="height: 5rem">-->
<span class="text-center my-3 text-2xl font-bold">营业端管理登录</span>
<el-form class="w-full flex flex-col items-end" label-width="80">
<el-form-item label="用户名" class="w-full">
<el-input v-model="username" placeholder="请输入用户名"/>
</el-form-item>
<el-form-item label="密码" class="w-full">
<el-input v-model="password" type="password" @keyup.enter.native="login"
placeholder="请输入密码"/>
</el-form-item>
<el-button @click.native="login" type="primary" class="w-11/12" color="#27AB49">登录</el-button>
</el-form>
</div>
</div>
</div>
</template>
<script setup>
import {ref} from 'vue'
import {useRouter} from 'vue-router'
import {loginReq} from '@/api/admin.js'
import {setUserInfo} from '@/api/common.js'
import {ElMessage} from 'element-plus'
const router = useRouter()
let username = ref(null)
let password = ref(null)
const login = async () => {
const res = await loginReq({username: username.value, password: password.value})
if (res.data) {
ElMessage.success({
message: '登陆成功'
});
setUserInfo(res.data)
await router.push('/Index')
}
}
</script>
<style scope>
.bg {
background-image: url("@/assets/bg.jpg");
background-size: cover;
background-repeat: no-repeat;
width: 100vw;
height: 100vh;
}
</style>

8
tailwind.config.cjs

@ -0,0 +1,8 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.vue"],
theme: {
extend: {},
},
plugins: [],
}

15
vite.base.config.js

@ -0,0 +1,15 @@
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
// gzip
import viteCompression from 'vite-plugin-compression'
import {resolve} from "path";
export default defineConfig({
plugins: [vue(), viteCompression()],
resolve: {
alias: {
'@': resolve(__dirname, "src")
}
},
})

19
vite.config.js

@ -0,0 +1,19 @@
import viteBaseConfig from './vite.base.config.js'
import viteDevConfig from './src/config/vite.dev.js'
import viteBuildConfig from './src/config/vite.build.js'
import {defineConfig} from "vite";
export default defineConfig(({mode}) => {
if (mode === "dev") {
return {
...viteBaseConfig,
...viteDevConfig,
};
} else {
return {
...viteBaseConfig,
...viteBuildConfig,
};
}
});
Loading…
Cancel
Save