访客页面

This commit is contained in:
zjf committed 2025-04-16 17:31:08 +08:00
1 parent a866ac71c0
commit 40de72ee99
8 files changed
+2146 -600

No files matched your search

+80 -48
View File
@@ -16,54 +16,86 @@ interface TabbarItem {
}
const tabbar: TabbarItem[] = [
{
pagePath: "pages/tabbar/canteen",
text: "食堂",
iconPath: "/static/tabbar/canteen.png",
selectedIconPath: "/static/tabbar/canteen-select.png",
launchParam: ["canteen"],
auth: [],
},
{
pagePath: "pages/tabbar/selfMerchant",
text: "自营商家",
iconPath: "/static/tabbar/selfMerchant.png",
selectedIconPath: "/static/tabbar/selfMerchant-select.png",
launchParam: ["selfMerchant"],
auth: [],
},
{
pagePath: "pages/tabbar/coopMerchant",
text: "合作商家",
iconPath: "/static/tabbar/coopMerchant.png",
selectedIconPath: "/static/tabbar/coopMerchant-select.png",
launchParam: ["selfMerchant"],
auth: ["user"],
},
{
pagePath: "pages/tabbar/order",
text: "订单",
iconPath: "/static/tabbar/order.png",
selectedIconPath: "/static/tabbar/order-select.png",
launchParam: ["canteen", "selfMerchant"],
auth: ["user"],
},
{
pagePath: "pages/tabbar/flowStatistics",
text: "流水统计",
iconPath: "/static/tabbar/order.png",
selectedIconPath: "/static/tabbar/order-select.png",
launchParam: [],
auth: ["merchant"],
},
{
pagePath: "pages/tabbar/individualOrder",
text: "订单",
iconPath: "/static/tabbar/order.png",
selectedIconPath: "/static/tabbar/order-select.png",
launchParam: [],
auth: ["merchant"],
},
// {
// pagePath: "pages/tabbar/canteen",
// text: "食堂",
// iconPath: "/static/tabbar/canteen.png",
// selectedIconPath: "/static/tabbar/canteen-select.png",
// launchParam: ["canteen"],
// auth: [],
// },
// {
// pagePath: "pages/tabbar/selfMerchant",
// text: "自营商家",
// iconPath: "/static/tabbar/selfMerchant.png",
// selectedIconPath: "/static/tabbar/selfMerchant-select.png",
// launchParam: ["selfMerchant"],
// auth: [],
// },
// {
// pagePath: "pages/tabbar/coopMerchant",
// text: "合作商家",
// iconPath: "/static/tabbar/coopMerchant.png",
// selectedIconPath: "/static/tabbar/coopMerchant-select.png",
// launchParam: ["selfMerchant"],
// auth: ["user"],
// },
// {
// pagePath: "pages/tabbar/order",
// text: "订单",
// iconPath: "/static/tabbar/order.png",
// selectedIconPath: "/static/tabbar/order-select.png",
// launchParam: ["canteen", "selfMerchant"],
// auth: ["user"],
// },
// {
// pagePath: "pages/tabbar/flowStatistics",
// text: "流水统计",
// iconPath: "/static/tabbar/order.png",
// selectedIconPath: "/static/tabbar/order-select.png",
// launchParam: [],
// auth: ["merchant"],
// },
// {
// pagePath: "pages/tabbar/individualOrder",
// text: "订单",
// iconPath: "/static/tabbar/order.png",
// selectedIconPath: "/static/tabbar/order-select.png",
// launchParam: [],
// auth: ["merchant"],
// },
{
pagePath: "pages/integrated/visitor/index",
text: "访客申请",
iconPath: "/static/tabbar/my.png",
selectedIconPath: "/static/tabbar/my-select.png",
launchParam: ["canteen", "selfMerchant"],
auth: ["user",],
},
{
pagePath: "pages/integrated/visitor/myvisitor",
text: "申请记录",
iconPath: "/static/tabbar/my.png",
selectedIconPath: "/static/tabbar/my-select.png",
launchParam: ["canteen", "selfMerchant"],
auth: ["user",],
},
{
pagePath: "pages/integrated/visitor/myyyvisitor",
text: "主动邀约",
iconPath: "/static/tabbar/my.png",
selectedIconPath: "/static/tabbar/my-select.png",
launchParam: ["canteen", "selfMerchant"],
auth: ["user",],
},
{
pagePath: "pages/integrated/visitor/myyyvisitorlist",
text: "邀约记录",
iconPath: "/static/tabbar/my.png",
selectedIconPath: "/static/tabbar/my-select.png",
launchParam: ["canteen", "selfMerchant"],
auth: ["user",],
},
{
pagePath: "pages/tabbar/my",
text: "我的",
+480 -451
View File
@@ -1,451 +1,480 @@
{
"easycom": {
"autoscan": true,
"custom": {
"^base-(.*)": "@/sheep/components/base/base-$1/base-$1.vue",
"^crud-(.*)": "@/sheep/components/crud/crud-$1/crud-$1.vue",
"^tm-(.*)": "@/uni_modules/tm-ui/components/tm-$1/tm-$1.vue",
"^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)": "z-paging/components/z-paging$1/z-paging$1.vue"
}
},
"pages": [
{
"path": "pages/tabbar/canteen",
"style": {
"navigationBarTitleText": "食堂",
"disableScroll": true
}
},
{
"path": "pages/tabbar/selfMerchant",
"style": {
"navigationBarTitleText": "自营商家",
"disableScroll": true
}
},
{
"path": "pages/tabbar/coopMerchant",
"style": {
"navigationBarTitleText": "合作商家",
"disableScroll": true
}
},
{
"path": "pages/tabbar/order",
"style": {
"navigationBarTitleText": "订单",
"disableScroll": true
}
},
{
"path": "pages/tabbar/flowStatistics",
"style": {
"navigationBarTitleText": "流水统计",
"disableScroll": true
}
},
{
"path": "pages/tabbar/individualOrder",
"style": {
"navigationBarTitleText": "订单",
"disableScroll": true
}
},
{
"path": "pages/tabbar/my",
"style": {
"navigationBarTitleText": "我的",
"disableScroll": true
}
}
],
"subPackages": [
{
"root": "pages/shop",
"pages": [
{
"path": "detail",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "brandDetail",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "product-detail",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/apply",
"pages": [
{
"path": "foreign-apply",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "foreign-list",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "foreign-items",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "stride-apply",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "stride-list",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "stride-items",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/boxes",
"pages": [
{
"path": "apply",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "detail",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "list",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "apply-list",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/merchant",
"pages": [
{
"path": "detail",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "food",
"style": {
"navigationBarTitleText": "菜谱",
"navigationBarBackgroundColor": "#1777FF",
"disableScroll": true
}
},
{
"path": "product-detail",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/order",
"pages": [
{
"path": "detail",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/refund",
"pages": [
{
"path": "apply",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "history",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/test",
"pages": [
{
"path": "login",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/wallet",
"pages": [
{
"path": "list",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "detail",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "recharge",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/human-face",
"pages": [
{
"path": "list",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "apply",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/user",
"pages": [
{
"path": "userRole",
"style": {
"navigationBarTitleText": "用户身份",
"disableScroll": true
}
}
]
},
{
"root": "pages/pay",
"pages": [
{
"path": "scanPay",
"style": {
"navigationBarTitleText": "支付",
"disableScroll": true
}
},
{
"path": "payComplete",
"style": {
"navigationBarTitleText": "支付完成",
"disableScroll": true
}
},
{
"path": "transferMoney",
"style": {
"navigationBarTitleText": "转账",
"disableScroll": true
}
}
]
},
{
"root": "pages/integrated",
"pages": [
{
"path": "feedback/index",
"style": {
"navigationBarTitleText": "意见反馈"
}
}
]
},
{
"root": "pages/integrated",
"pages": [
{
"path": "visitor/index",
"style": {
"navigationBarTitleText": "访客"
}
}
]
},
{
"root": "pages/integrated",
"pages": [
{
"path": "visitor/myvisitor",
"style": {
"navigationBarTitleText": "访客记录"
}
}
]
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "浦江小程序",
"navigationBarBackgroundColor": "#1777FF",
"backgroundColor": "#ecf4f9",
"navigationStyle": "custom"
},
"tabBar": {
"list": [
{
"pagePath": "pages/tabbar/canteen",
"text": "",
"iconPath": "",
"selectedIconPath": ""
},
{
"pagePath": "pages/tabbar/selfMerchant",
"text": "",
"iconPath": "",
"selectedIconPath": ""
},
{
"pagePath": "pages/tabbar/coopMerchant",
"text": "",
"iconPath": "",
"selectedIconPath": ""
},
{
"pagePath": "pages/tabbar/order",
"text": "",
"iconPath": "",
"selectedIconPath": ""
},
{
"pagePath": "pages/tabbar/flowStatistics",
"text": "",
"iconPath": "",
"selectedIconPath": ""
},
{
"pagePath": "pages/tabbar/individualOrder",
"text": "",
"iconPath": "",
"selectedIconPath": ""
},
{
"pagePath": "pages/tabbar/my",
"text": "",
"iconPath": "",
"selectedIconPath": ""
}
// {
// "pagePath": "pages/tabbar/canteen",
// "text": "食堂",
// "iconPath": "static/tabbar/canteen.png",
// "selectedIconPath": "static/tabbar/canteen-select.png"
// },
// {
// "pagePath": "pages/tabbar/selfMerchant",
// "text": "自营商家",
// "iconPath": "static/tabbar/selfMerchant.png",
// "selectedIconPath": "static/tabbar/selfMerchant-select.png"
// },
// {
// pagePath: "pages/tabbar/coopMerchant",
// text: "合作商家",
// iconPath: "static/tabbar/coopMerchant.png",
// selectedIconPath: "static/tabbar/coopMerchant-select.png",
// },
// {
// "pagePath": "pages/tabbar/order",
// "text": "订单",
// "iconPath": "static/tabbar/order.png",
// "selectedIconPath": "static/tabbar/order-select.png"
// },
// {
// "pagePath": "pages/tabbar/flowStatistics",
// "text": "流水统计",
// "iconPath": "static/tabbar/order.png",
// "selectedIconPath": "static/tabbar/order-select.png"
// },
// {
// "pagePath": "pages/tabbar/individualOrder",
// "text": "订单",
// "iconPath": "static/tabbar/order.png",
// "selectedIconPath": "static/tabbar/order-select.png"
// },
// {
// "pagePath": "pages/tabbar/my",
// "text": "我的",
// "iconPath": "static/tabbar/my.png",
// "selectedIconPath": "static/tabbar/my-select.png"
// },
]
}
}
{
"easycom": {
"autoscan": true,
"custom": {
"^base-(.*)": "@/sheep/components/base/base-$1/base-$1.vue",
"^crud-(.*)": "@/sheep/components/crud/crud-$1/crud-$1.vue",
"^tm-(.*)": "@/uni_modules/tm-ui/components/tm-$1/tm-$1.vue",
"^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)": "z-paging/components/z-paging$1/z-paging$1.vue"
}
},
"pages": [
{
"path": "pages/tabbar/canteen",
"style": {
"navigationBarTitleText": "食堂",
"disableScroll": true
}
},
{
"path": "pages/tabbar/selfMerchant",
"style": {
"navigationBarTitleText": "自营商家",
"disableScroll": true
}
},
{
"path": "pages/tabbar/coopMerchant",
"style": {
"navigationBarTitleText": "合作商家",
"disableScroll": true
}
},
{
"path": "pages/tabbar/order",
"style": {
"navigationBarTitleText": "订单",
"disableScroll": true
}
},
// {
// "path": "pages/tabbar/flowStatistics",
// "style": {
// "navigationBarTitleText": "流水统计",
// "disableScroll": true
// }
// },
// {
// "path": "pages/tabbar/individualOrder",
// "style": {
// "navigationBarTitleText": "订单",
// "disableScroll": true
// }
// },
// {
// "path": "pages/integrated/visitor/myvisitor",
// "style": {
// "navigationBarTitleText": "访客",
// "disableScroll": true
// }
// },
{
"path": "pages/tabbar/my",
"style": {
"navigationBarTitleText": "我的",
"disableScroll": true
}
}
],
"subPackages": [
{
"root": "pages/shop",
"pages": [
{
"path": "detail",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "brandDetail",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "product-detail",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/apply",
"pages": [
{
"path": "foreign-apply",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "foreign-list",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "foreign-items",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "stride-apply",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "stride-list",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "stride-items",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/boxes",
"pages": [
{
"path": "apply",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "detail",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "list",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "apply-list",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/merchant",
"pages": [
{
"path": "detail",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "food",
"style": {
"navigationBarTitleText": "菜谱",
"navigationBarBackgroundColor": "#1777FF",
"disableScroll": true
}
},
{
"path": "product-detail",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/order",
"pages": [
{
"path": "detail",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/refund",
"pages": [
{
"path": "apply",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "history",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/test",
"pages": [
{
"path": "login",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/wallet",
"pages": [
{
"path": "list",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "detail",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "recharge",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/human-face",
"pages": [
{
"path": "list",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
},
{
"path": "apply",
"style": {
"navigationBarTitleText": "",
"disableScroll": true
}
}
]
},
{
"root": "pages/user",
"pages": [
{
"path": "userRole",
"style": {
"navigationBarTitleText": "用户身份",
"disableScroll": true
}
}
]
},
{
"root": "pages/pay",
"pages": [
{
"path": "scanPay",
"style": {
"navigationBarTitleText": "支付",
"disableScroll": true
}
},
{
"path": "payComplete",
"style": {
"navigationBarTitleText": "支付完成",
"disableScroll": true
}
},
{
"path": "transferMoney",
"style": {
"navigationBarTitleText": "转账",
"disableScroll": true
}
}
]
},
{
"root": "pages/integrated",
"pages": [
{
"path": "feedback/index",
"style": {
"navigationBarTitleText": "意见反馈"
}
}
]
},
{
"root": "pages/integrated",
"pages": [
{
"path": "visitor/index",
"style": {
"navigationBarTitleText": "访客"
}
}
]
},
{
"root": "pages/integrated",
"pages": [
{
"path": "visitor/myvisitor",
"style": {
"navigationBarTitleText": "访客记录"
}
}
]
},
{
"root": "pages/integrated",
"pages": [
{
"path": "visitor/myyyvisitor",
"style": {
"navigationBarTitleText": "邀约访客"
}
}
]
},
{
"root": "pages/integrated",
"pages": [
{
"path": "visitor/myyyvisitorlist",
"style": {
"navigationBarTitleText": "邀约记录"
}
}
]
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "浦江小程序",
"navigationBarBackgroundColor": "#1777FF",
"backgroundColor": "#ecf4f9",
"navigationStyle": "custom"
},
"tabBar": {
"list": [
{
"pagePath": "pages/tabbar/canteen",
"text": "",
"iconPath": "",
"selectedIconPath": ""
},
{
"pagePath": "pages/tabbar/selfMerchant",
"text": "",
"iconPath": "",
"selectedIconPath": ""
},
{
"pagePath": "pages/tabbar/coopMerchant",
"text": "",
"iconPath": "",
"selectedIconPath": ""
},
{
"pagePath": "pages/tabbar/order",
"text": "",
"iconPath": "",
"selectedIconPath": ""
},
{
"pagePath": "pages/tabbar/flowStatistics",
"text": "",
"iconPath": "",
"selectedIconPath": ""
},
{
"pagePath": "pages/tabbar/individualOrder",
"text": "",
"iconPath": "",
"selectedIconPath": ""
},
{
"pagePath": "pages/tabbar/my",
"text": "",
"iconPath": "",
"selectedIconPath": ""
}
// {
// "pagePath": "pages/tabbar/canteen",
// "text": "食堂",
// "iconPath": "static/tabbar/canteen.png",
// "selectedIconPath": "static/tabbar/canteen-select.png"
// },
// {
// "pagePath": "pages/tabbar/selfMerchant",
// "text": "自营商家",
// "iconPath": "static/tabbar/selfMerchant.png",
// "selectedIconPath": "static/tabbar/selfMerchant-select.png"
// },
// {
// pagePath: "pages/tabbar/coopMerchant",
// text: "合作商家",
// iconPath: "static/tabbar/coopMerchant.png",
// selectedIconPath: "static/tabbar/coopMerchant-select.png",
// },
// {
// "pagePath": "pages/tabbar/order",
// "text": "订单",
// "iconPath": "static/tabbar/order.png",
// "selectedIconPath": "static/tabbar/order-select.png"
// },
// {
// "pagePath": "pages/tabbar/flowStatistics",
// "text": "流水统计",
// "iconPath": "static/tabbar/order.png",
// "selectedIconPath": "static/tabbar/order-select.png"
// },
// {
// "pagePath": "pages/tabbar/individualOrder",
// "text": "订单",
// "iconPath": "static/tabbar/order.png",
// "selectedIconPath": "static/tabbar/order-select.png"
// },
// {
// "pagePath": "pages/tabbar/my",
// "text": "我的",
// "iconPath": "static/tabbar/my.png",
// "selectedIconPath": "static/tabbar/my-select.png"
// },
]
}
}
+117 -2
View File
@@ -48,6 +48,7 @@
<view class="form-item">
<text class="label">公司名称</text>
<text class="required">*</text>
<input
type="text"
v-model="formData.companyName"
@@ -57,6 +58,22 @@
/>
</view>
<view class="form-item">
<text class="label">拜访原因<text class="required">*</text></text>
<picker
:range="reasonList"
range-key="label"
@change="onReasonChange"
>
<view class="input-wrapper">
<text :class="['placeholder', formData.reasonValue ? 'active' : '']">
{{ formData.reasonLabel || '请选择拜访原因' }}
</text>
<text class="arrow">></text>
</view>
</picker>
</view>
<view class="form-item switch-item">
<text class="label">是否有随行人员</text>
<switch
@@ -151,8 +168,10 @@
<view class="bottom-space"></view>
</scroll-view>
<!-- 提交按钮 -->
<view class="submit-btn" @tap="handleSubmit">确定</view>
<!-- 底部按钮区域 -->
<view class="bottom-safe-area">
<button class="submit-btn" @tap="handleSubmit">提交</button>
</view>
<!-- 部门选择弹窗 -->
<view class="popup" v-if="showDeptPopup">
@@ -290,6 +309,8 @@ const formData = reactive({
vEndDate: '',
type: '1',
checkStatus: '0',
reasonValue: '',
reasonLabel: '',
})
// 随行人员列表
@@ -320,6 +341,9 @@ const selectedUserId = ref('')
const selectedUserName = ref('')
const searchKeyword = ref('')
// 拜访原因相关
const reasonList = ref<any[]>([])
// 生成未来一周的日期
const dateList = computed(() => {
const dates = []
@@ -450,6 +474,10 @@ const handleSubmit = async () => {
uni.showToast({ title: '请选择到访时间', icon: 'none' })
return
}
if (!formData.reasonValue || !formData.reasonLabel) {
uni.showToast({ title: '请选择拜访原因', icon: 'none' })
return
}
try {
const submitData = {
@@ -629,9 +657,30 @@ const closeUserPicker = () => {
// 记录当前选中的部门ID,用于判断是否需要重新获取用户列表
const currentDeptId = ref('')
// 获取拜访原因列表
const getReasonList = async () => {
try {
const { code, data } = await visitorApi.getDictListByType('1')
if (code === 0 && data) {
// 将对象转换为数组
reasonList.value = Object.values(data)
}
} catch (error) {
console.error('获取拜访原因失败:', error)
}
}
// 拜访原因选择变化
const onReasonChange = (e: any) => {
const selected = reasonList.value[e.detail.value]
formData.reasonValue = selected.value
formData.reasonLabel = selected.label
}
// 页面加载时获取部门数据
onMounted(() => {
getDeptList()
getReasonList()
})
</script>
@@ -1044,4 +1093,70 @@ onMounted(() => {
.bottom-space {
height: 180rpx;
}
.picker-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx 32rpx;
background-color: #f7f7f7;
text {
font-size: 28rpx;
&:first-child {
color: #666;
}
&:last-child {
color: #1989fa;
}
}
}
.picker-view {
width: 100%;
height: 392rpx;
background-color: #fff;
}
.picker-item {
line-height: 88rpx;
text-align: center;
font-size: 32rpx;
}
.placeholder {
color: #999;
&.active {
color: #333;
}
}
.arrow {
color: #999;
font-size: 28rpx;
}
.bottom-safe-area {
padding: 20rpx 32rpx;
padding-bottom: calc(20rpx + constant(safe-area-inset-bottom)); /* iOS 11.0 */
padding-bottom: calc(20rpx + env(safe-area-inset-bottom)); /* iOS 11.2 */
background: #fff;
}
.submit-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background: #1989fa;
color: #fff;
font-size: 32rpx;
border-radius: 44rpx;
&:active {
opacity: 0.8;
}
}
</style>
+228 -99
View File
@@ -17,45 +17,73 @@
<scroll-view
scroll-y
class="list-content"
:refresher-enabled="true"
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
@scrolltolower="loadMore"
>
<view
v-for="(item, index) in visitorList"
:key="index"
v-for="item in visitorList"
:key="item.id"
class="visitor-item"
>
<view class="item-header">
<text class="visit-time">{{ item.visitTime }}</text>
<text :class="['status-tag', getStatusClass(item.status)]">{{ getStatusText(item.status) }}</text>
<text class="visit-time">{{ formatTime(item.vstartDate, item.vendDate) }}</text>
<text :class="['status-tag', getStatusClass(item.checkStatus)]">
{{ getStatusText(item.checkStatus) }}
</text>
</view>
<view class="item-content">
<view class="info-row">
<text class="label">拜访部门:</text>
<text class="value">{{ item.deptName }}</text>
<text class="label">接待人:</text>
<text class="value">{{ item.userName }}</text>
</view>
<view class="info-row">
<text class="label">拜访人:</text>
<text class="value">{{ item.name }}</text>
</view>
<view class="info-row">
<text class="label">手机号:</text>
<text class="label">联系方式:</text>
<text class="value">{{ item.mobile }}</text>
</view>
<view class="info-row">
<text class="label">预约时间:</text>
<text class="value">{{ formatCreateTime(item.createTime) }}</text>
</view>
<view v-if="item.description" class="info-row">
<text class="label">备注:</text>
<text class="value">{{ item.description }}</text>
</view>
<view v-if="item.checkReason" class="info-row check-reason">
<text class="label">审核意见:</text>
<text class="value">{{ item.checkReason }}</text>
</view>
</view>
<view class="item-footer" v-if="item.status === '0' || item.status === '1'">
<view class="cancel-btn" @tap="handleCancel(item)">取消</view>
<view class="item-footer" v-if="canCancel(item.checkStatus) || canVisit(item.checkStatus)">
<view class="btn-group">
<view
v-if="canVisit(item.checkStatus)"
class="visit-btn"
@tap="handleVisit(item)"
>已到访</view>
<view
v-if="canCancel(item.checkStatus)"
class="cancel-btn"
@tap="handleCancel(item)"
>取消</view>
</view>
</view>
</view>
<!-- 加载状态 -->
<view v-if="loading" class="loading">加载中...</view>
<view v-if="loading && !refreshing" class="loading">加载中...</view>
<view v-if="!loading && !hasMore" class="no-more">没有更多数据了</view>
<view v-if="!loading && visitorList.length === 0" class="empty">
暂无访客记录
</view>
</scroll-view>
</view>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import visitorApi from '@/sheep/api/integrated/visitors'
// 标签页配置
const tabs = ['全部', '待审核', '待到访', '已到访']
@@ -64,37 +92,92 @@ const currentTab = ref(0)
// 列表数据
const visitorList = ref<any[]>([])
const loading = ref(false)
const refreshing = ref(false)
const hasMore = ref(true)
const page = ref(1)
const pageSize = 10
const current = ref(1)
const size = 10
// 格式化访问时间
const formatTime = (start: string, end: string) => {
if (!start || !end) return ''
const startDate = new Date(start)
const endDate = new Date(end)
return `${startDate.getMonth() + 1}/${startDate.getDate()} ${startDate.getHours()}:00-${endDate.getHours()}:00`
}
// 格式化创建时间
const formatCreateTime = (time: string) => {
if (!time) return ''
const date = new Date(time)
return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${date.getMinutes()}`
}
// 获取状态文本
const getStatusText = (status: string) => {
const statusMap: Record<string, string> = {
'1': '待审核',
'2': '待到访',
'0': '已到访'
}
return statusMap[status] || status
}
// 获取状态样式
const getStatusClass = (status: string) => {
const statusMap: Record<string, string> = {
'1': 'status-pending',
'2': 'status-waiting',
'0': 'status-visited'
}
return statusMap[status] || ''
}
// 切换标签页
const switchTab = (index: number) => {
if (currentTab.value === index) return
currentTab.value = index
page.value = 1
resetList()
getVisitorList()
}
// 重置列表状态
const resetList = () => {
current.value = 1
visitorList.value = []
hasMore.value = true
getVisitorList()
}
// 获取访客记录列表
const getVisitorList = async () => {
if (loading.value || !hasMore.value) return
if (loading.value || (!hasMore.value && !refreshing.value)) return
loading.value = true
try {
// TODO: 替换为实际的API调用
const res = await mockFetchData()
if (page.value === 1) {
visitorList.value = res.list
} else {
visitorList.value.push(...res.list)
const params: any = {
current: current.value,
size
}
hasMore.value = res.list.length === pageSize
page.value++
// 添加状态筛选
if (currentTab.value > 0) {
const statusMap = ['', '1', '2', '0']
params.checkStatus = statusMap[currentTab.value]
}
const { code, data } = await visitorApi.getVisitorList(params)
if (code === 0 && data) {
if (refreshing.value || current.value === 1) {
visitorList.value = data.records || []
} else {
visitorList.value.push(...(data.records || []))
}
hasMore.value = (data.records || []).length === size
if (hasMore.value) {
current.value++
}
}
} catch (error) {
console.error('获取访客记录失败:', error)
uni.showToast({
@@ -103,82 +186,69 @@ const getVisitorList = async () => {
})
} finally {
loading.value = false
refreshing.value = false
}
}
// 加载更多
// 下拉刷新
const onRefresh = async () => {
refreshing.value = true
resetList()
await getVisitorList()
}
// 上拉加载更多
const loadMore = () => {
getVisitorList()
}
// 获取状态文本
const getStatusText = (status: string) => {
const statusMap: Record<string, string> = {
'0': '待审核',
'1': '待到访',
'2': '已到访',
'3': '已过期'
if (!loading.value && hasMore.value) {
getVisitorList()
}
return statusMap[status] || status
}
// 获取状态样式类
const getStatusClass = (status: string) => {
const statusClassMap: Record<string, string> = {
'0': 'status-pending',
'1': 'status-waiting',
'2': 'status-visited',
'3': 'status-expired'
}
return statusClassMap[status] || ''
// 判断是否可以取消
const canCancel = (status: string) => {
return status === '1' || status === '2'
}
// 模拟数据获取
const mockFetchData = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
list: [
{
visitTime: '2023/02/13 周一 11:00',
status: '0',
deptName: 'XXXX丁地1',
name: 'AAA',
mobile: '13590291111'
},
{
visitTime: '2023/02/13 周一 08:00',
status: '1',
deptName: 'XXXX丁地2',
name: 'BBB',
mobile: '13090291111'
},
// 更多数据...
]
})
}, 500)
})
// 判断是否可以标记到访
const canVisit = (status: string) => {
return status === '2'
}
// 添加取消方法
// 修改取消预约方法
const handleCancel = (item: any) => {
if (!item.id) return
uni.showModal({
title: '提示',
content: '确定要取消此次预约吗?',
success: async (res) => {
if (res.confirm) {
try {
// TODO: 调用取消预约的接口
// const result = await cancelVisit(item.id)
uni.showToast({
title: '取消成功',
icon: 'success'
uni.showLoading({
title: '取消中...',
mask: true
})
// 刷新列表
page.value = 1
visitorList.value = []
getVisitorList()
const { code, msg } = await visitorApi.cancelVisitor(item.id)
uni.hideLoading()
if (code === 0) {
uni.showToast({
title: '取消成功',
icon: 'success'
})
// 刷新列表
resetList()
getVisitorList()
} else {
uni.showToast({
title: msg || '取消失败',
icon: 'none'
})
}
} catch (error) {
uni.hideLoading()
uni.showToast({
title: '取消失败',
icon: 'none'
@@ -189,6 +259,51 @@ const handleCancel = (item: any) => {
})
}
// 处理到访
const handleVisit = (item: any) => {
if (!item.id) return
uni.showModal({
title: '提示',
content: '确认访客已到访?',
success: async (res) => {
if (res.confirm) {
try {
uni.showLoading({
title: '处理中...',
mask: true
})
const { code, msg } = await visitorApi.haveVisitor(item.id)
uni.hideLoading()
if (code === 0) {
uni.showToast({
title: '操作成功',
icon: 'success'
})
// 刷新列表
resetList()
getVisitorList()
} else {
uni.showToast({
title: msg || '操作失败',
icon: 'none'
})
}
} catch (error) {
uni.hideLoading()
uni.showToast({
title: '操作失败',
icon: 'none'
})
}
}
}
})
}
onMounted(() => {
getVisitorList()
})
@@ -248,10 +363,6 @@ onMounted(() => {
border-radius: 12rpx;
padding: 24rpx;
margin-bottom: 20rpx;
.item-content {
margin: 20rpx 0;
}
}
.item-header {
@@ -286,11 +397,6 @@ onMounted(() => {
color: #07C160;
background: rgba(7, 193, 96, 0.1);
}
&.status-expired {
color: #999;
background: rgba(153, 153, 153, 0.1);
}
}
.info-row {
@@ -305,6 +411,7 @@ onMounted(() => {
font-size: 26rpx;
color: #666;
width: 140rpx;
flex-shrink: 0;
}
.value {
@@ -312,29 +419,51 @@ onMounted(() => {
color: #333;
flex: 1;
}
&.check-reason {
margin-top: 16rpx;
padding-top: 16rpx;
border-top: 1rpx solid #F5F5F5;
.value {
color: #FF4D4F;
}
}
}
.item-footer {
margin-top: 20rpx;
padding-top: 20rpx;
border-top: 1rpx solid #F5F5F5;
display: flex;
justify-content: flex-end;
}
.cancel-btn {
.btn-group {
display: flex;
justify-content: flex-end;
gap: 20rpx;
}
.cancel-btn, .visit-btn {
padding: 12rpx 32rpx;
border: 1rpx solid #DCDEE0;
border-radius: 32rpx;
font-size: 26rpx;
color: #666;
&:active {
opacity: 0.8;
}
}
.loading, .no-more {
.cancel-btn {
border: 1rpx solid #DCDEE0;
color: #666;
}
.visit-btn {
background: #1989FA;
color: #fff;
}
.loading, .no-more, .empty {
text-align: center;
font-size: 24rpx;
color: #999;
@@ -0,0 +1,672 @@
<template>
<view class="page-container">
<view class="form-header">
<text>我要邀约</text>
</view>
<view class="form-content">
<view class="form-item">
<text class="label">访客姓名<text class="required">*</text></text>
<input
class="uni-input"
type="text"
v-model="form.name"
placeholder="请输入真实姓名"
placeholder-style="color: #999999;"
/>
</view>
<view class="form-item">
<text class="label">访客手机号<text class="required">*</text></text>
<input
class="uni-input"
type="number"
v-model="form.mobile"
placeholder="请输入手机号"
placeholder-style="color: #999999;"
/>
</view>
<view class="form-item">
<text class="label">来访公司名称</text>
<input
class="uni-input"
type="text"
v-model="form.companyName"
placeholder="请输入公司名称"
placeholder-style="color: #999999;"
/>
</view>
<view class="form-item select-item" @tap="showTimePicker">
<text class="label">拜访时间<text class="required">*</text></text>
<view class="select-right">
<text class="select-value">{{ selectedTime || '请选择' }}</text>
<text class="select-arrow">></text>
</view>
</view>
<view class="form-item select-item" @tap="showDeptPicker">
<text class="label">拜访部门<text class="required">*</text></text>
<view class="select-right">
<text class="select-value">{{ selectedDeptName || '请选择' }}</text>
<text class="select-arrow">></text>
</view>
</view>
<view class="form-item">
<text class="label">拜访原因<text class="required">*</text></text>
<picker
:range="reasonList"
range-key="label"
@change="onReasonChange"
>
<view class="input-wrapper">
<text class="input-text">{{ form.reasonLabel || '请选择拜访原因' }}</text>
<text class="arrow">></text>
</view>
</picker>
</view>
<view class="form-item switch-item">
<text class="label">是否可随行人员</text>
<switch
:checked="form.hasFollower"
@change="onSwitchChange"
color="#1989fa"
/>
</view>
</view>
<view class="submit-button">
<button class="btn" @tap="handleSubmit">提交</button>
</view>
<view class="popup" v-if="showTimePopup">
<view class="popup-mask" @tap="closeTimePicker"></view>
<view class="popup-content">
<view class="popup-header">
<text class="popup-title">拜访时间</text>
<text class="popup-close" @tap="closeTimePicker">×</text>
</view>
<view class="time-picker">
<view class="date-list">
<view
v-for="(date, dateIndex) in dateList"
:key="dateIndex"
class="date-item"
:class="{ active: selectedDateIndex === dateIndex }"
@tap="selectDate(dateIndex)"
>
<text class="date-week">{{ date.weekDay }}</text>
<text class="date-day">{{ date.date }}</text>
</view>
</view>
<view class="time-list">
<view
v-for="(time, timeIndex) in timeList"
:key="timeIndex"
class="time-item"
:class="{ active: selectedTimeIndex === timeIndex }"
@tap="selectTime(timeIndex)"
>
{{ time }}
</view>
</view>
</view>
<view class="popup-footer">
<view class="confirm-btn" @tap="confirmTimeSelect">确定</view>
</view>
</view>
</view>
<view class="popup" v-if="showDeptPopup">
<view class="popup-mask" @tap="closeDeptPicker"></view>
<view class="popup-content">
<view class="popup-header">
<text class="popup-title">选择拜访部门</text>
<text class="popup-close" @tap="closeDeptPicker">×</text>
</view>
<scroll-view scroll-y class="dept-list">
<view
v-for="(dept, index) in flatDeptList"
:key="dept.id"
class="dept-item"
:class="{
'active': selectedDeptId === dept.id,
'level-1': dept.level === 1,
'level-2': dept.level === 2,
'level-3': dept.level === 3
}"
@tap="selectDept(dept)"
>
<text class="dept-name">{{ dept.name }}</text>
</view>
</scroll-view>
<view class="popup-footer">
<view class="confirm-btn" @tap="confirmDeptSelect">确定</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, computed } from 'vue'
import visitorApi from '@/sheep/api/integrated/visitors'
import dayjs from 'dayjs'
const showTimePopup = ref(false)
const showDeptPopup = ref(false)
const selectedDateIndex = ref(0)
const selectedTimeIndex = ref(-1)
const selectedTime = ref('')
const deptList = ref<any[]>([])
const flatDeptList = ref<any[]>([])
const selectedDeptId = ref('')
const selectedDeptName = ref('')
const form = reactive({
name: '',
mobile: '',
companyName: '',
vstartDate: '',
vEndDate: '',
deptId: '',
deptName: '',
reasonValue: '',
reasonLabel: '',
hasFollower: false
})
const reasonList = ref<any[]>([])
const getReasonList = async () => {
try {
const { code, data } = await visitorApi.getDictListByType(1)
if (code === 0 && data) {
reasonList.value = data.map((item: any) => ({
value: item.value,
label: item.label
}))
}
} catch (error) {
console.error('获取拜访原因列表失败:', error)
}
}
const dateList = computed(() => {
const dates = []
const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
for (let i = 0; i < 7; i++) {
const date = dayjs().add(i, 'day')
dates.push({
date: date.format('MM.DD'),
weekDay: weekDays[date.day()],
fullDate: date.format('YYYY-MM-DD')
})
}
return dates
})
const timeList = computed(() => {
const times = []
for (let i = 8; i < 18; i++) {
times.push(`${i.toString().padStart(2, '0')}:00-${(i + 1).toString().padStart(2, '0')}:00`)
}
return times
})
const getDeptList = async () => {
try {
const { code, data } = await visitorApi.getSimpleTree()
if (code === 0 && data) {
deptList.value = data
flattenDeptList(data)
}
} catch (error) {
console.error('获取部门列表失败:', error)
}
}
const flattenDeptList = (depts: any[], level = 1, result: any[] = []) => {
depts.forEach(dept => {
result.push({
id: dept.id,
name: dept.name,
level
})
if (dept.children && dept.children.length > 0) {
flattenDeptList(dept.children, level + 1, result)
}
})
flatDeptList.value = result
}
const showTimePicker = () => {
showTimePopup.value = true
}
const closeTimePicker = () => {
showTimePopup.value = false
}
const selectDate = (index: number) => {
selectedDateIndex.value = index
}
const selectTime = (index: number) => {
selectedTimeIndex.value = index
}
const confirmTimeSelect = () => {
if (selectedTimeIndex.value === -1) {
uni.showToast({
title: '请选择具体时间',
icon: 'none'
})
return
}
const selectedDate = dateList.value[selectedDateIndex.value]
const selectedTimeRange = timeList.value[selectedTimeIndex.value]
const [startTime, endTime] = selectedTimeRange.split('-')
form.vstartDate = `${selectedDate.fullDate} ${startTime}:00`
form.vEndDate = `${selectedDate.fullDate} ${endTime}:00`
selectedTime.value = `${selectedDate.weekDay} ${selectedDate.date} ${selectedTimeRange}`
closeTimePicker()
}
const showDeptPicker = () => {
if (flatDeptList.value.length === 0) {
getDeptList()
}
showDeptPopup.value = true
}
const closeDeptPicker = () => {
showDeptPopup.value = false
}
const selectDept = (dept: any) => {
selectedDeptId.value = dept.id
selectedDeptName.value = dept.name
}
const confirmDeptSelect = () => {
if (!selectedDeptId.value) {
uni.showToast({
title: '请选择拜访部门',
icon: 'none'
})
return
}
form.deptId = selectedDeptId.value
form.deptName = selectedDeptName.value
closeDeptPicker()
}
const onReasonChange = (e: any) => {
const selected = reasonList.value[e.detail.value]
form.reasonValue = selected.value
form.reasonLabel = selected.label
}
const onSwitchChange = (e: any) => {
form.hasFollower = e.detail.value
}
const handleSubmit = async () => {
// 表单验证
if (!form.name) {
uni.showToast({
title: '请输入访客姓名',
icon: 'none'
})
return
}
if (!form.mobile) {
uni.showToast({
title: '请输入手机号',
icon: 'none'
})
return
}
if (!form.vstartDate) {
uni.showToast({
title: '请选择拜访时间',
icon: 'none'
})
return
}
if (!form.deptId) {
uni.showToast({
title: '请选择拜访部门',
icon: 'none'
})
return
}
if (!form.reasonValue) {
uni.showToast({
title: '请选择拜访原因',
icon: 'none'
})
return
}
try {
const { code, msg } = await visitorApi.saveYY(form)
if (code === 0) {
uni.showToast({
title: '提交成功',
icon: 'success'
})
// 重置表单
Object.assign(form, {
name: '',
mobile: '',
companyName: '',
vstartDate: '',
vEndDate: '',
deptId: '',
deptName: '',
reasonValue: '',
reasonLabel: '',
hasFollower: false
})
selectedTime.value = ''
selectedDeptName.value = ''
} else {
uni.showToast({
title: msg || '提交失败',
icon: 'none'
})
}
} catch (error) {
console.error('提交失败:', error)
uni.showToast({
title: '提交失败',
icon: 'none'
})
}
}
onMounted(() => {
getDeptList()
getReasonList()
})
</script>
<style lang="scss" scoped>
.page-container {
min-height: 100vh;
background-color: #f5f5f5;
padding-top: 20rpx;
}
.form-header {
background: #fff;
padding: 30rpx;
font-size: 32rpx;
color: #333;
margin-bottom: 20rpx;
}
.form-content {
background: #fff;
}
.form-item {
padding: 30rpx;
border-bottom: 1rpx solid #eee;
.label {
display: block;
margin-bottom: 16rpx;
font-size: 28rpx;
color: #333;
.required {
color: #ff4d4f;
margin-left: 4rpx;
}
}
.uni-input {
width: 100%;
height: 44rpx;
font-size: 28rpx;
color: #333;
}
.input-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
.input-text {
font-size: 28rpx;
color: #999;
}
.arrow {
color: #999;
font-size: 28rpx;
}
}
&.switch-item {
display: flex;
justify-content: space-between;
align-items: center;
.label {
margin-bottom: 0;
}
}
}
.submit-button {
padding: 40rpx 30rpx;
.btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background: #1989fa;
color: #fff;
font-size: 32rpx;
border-radius: 8rpx;
}
}
.popup {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 100;
.popup-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.4);
}
.popup-content {
position: absolute;
left: 0;
right: 0;
bottom: 0;
background: #fff;
border-radius: 24rpx 24rpx 0 0;
padding-bottom: env(safe-area-inset-bottom);
}
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx;
border-bottom: 1rpx solid #f5f5f5;
.popup-title {
font-size: 32rpx;
color: #323233;
font-weight: 500;
}
.popup-close {
font-size: 40rpx;
color: #969799;
padding: 10rpx;
}
}
.time-picker {
padding: 24rpx 0;
}
.date-list {
display: flex;
padding: 0 24rpx;
margin-bottom: 24rpx;
.date-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 16rpx 0;
&.active {
background: #E8F3FF;
border-radius: 8rpx;
.date-week, .date-day {
color: #1989FA;
}
}
.date-week {
font-size: 28rpx;
color: #323233;
margin-bottom: 8rpx;
}
.date-day {
font-size: 26rpx;
color: #969799;
}
}
}
.time-list {
padding: 0 24rpx;
display: flex;
flex-wrap: wrap;
gap: 20rpx;
.time-item {
width: calc((100% - 40rpx) / 3);
height: 72rpx;
display: flex;
align-items: center;
justify-content: center;
background: #F7F8FA;
border-radius: 8rpx;
font-size: 28rpx;
color: #323233;
&.active {
background: #E8F3FF;
color: #1989FA;
}
}
}
.popup-footer {
padding: 24rpx 32rpx;
border-top: 1rpx solid #f5f5f5;
.confirm-btn {
height: 88rpx;
background: #1989FA;
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 32rpx;
font-weight: 500;
}
}
.dept-list {
max-height: 60vh;
padding: 20rpx 0;
}
.dept-item {
padding: 24rpx 32rpx;
font-size: 28rpx;
color: #323233;
&.active {
color: #1989FA;
background: #F5F5F5;
}
&.level-1 {
font-weight: 500;
}
&.level-2 {
padding-left: 64rpx;
}
&.level-3 {
padding-left: 96rpx;
}
}
.select-item {
display: flex;
justify-content: space-between;
align-items: center;
}
.select-right {
display: flex;
align-items: center;
.select-value {
font-size: 30rpx;
color: #969799;
}
.select-arrow {
margin-left: 12rpx;
font-size: 28rpx;
color: #969799;
}
}
</style>
@@ -0,0 +1,472 @@
<template>
<view class="page-container">
<!-- 顶部状态切换 -->
<view class="status-tabs">
<view
v-for="(tab, index) in tabs"
:key="index"
:class="['tab-item', { active: currentTab === tab.value }]"
@tap="handleTabChange(tab.value)"
>
{{ tab.label }}
</view>
</view>
<view class="visitor-list">
<view
v-for="(item, index) in visitorList"
:key="index"
class="visitor-item"
>
<view class="visitor-header">
<text class="visit-time">{{ formatDateTime(item.vStartDate) }}</text>
<text :class="['status-tag', getStatusClass(item.checkStatus)]">{{ getStatusText(item.checkStatus) }}</text>
</view>
<view class="visitor-content">
<view class="info-row">
<text class="label">访客姓名:</text>
<text class="value">{{ item.name }}</text>
</view>
<view class="info-row">
<text class="label">手机号码:</text>
<text class="value">{{ item.mobile }}</text>
</view>
<view class="info-row">
<text class="label">拜访部门:</text>
<text class="value">{{ item.deptName }}</text>
</view>
<view class="info-row" v-if="item.companyName">
<text class="label">来访公司:</text>
<text class="value">{{ item.companyName }}</text>
</view>
<view class="info-row">
<text class="label">拜访时间:</text>
<text class="value">{{ formatDateTime(item.vStartDate) }} - {{ formatDateTime(item.vEndDate) }}</text>
</view>
<view class="info-row">
<text class="label">拜访原因:</text>
<text class="value">{{ getReasonText(item.description) }}</text>
</view>
</view>
<view class="visitor-footer">
<template v-if="item.checkStatus === '1'">
<view class="action-btn reject-btn" @tap="handleReject(item)">拒绝</view>
<view class="action-btn accept-btn" @tap="handleAccept(item)">接受</view>
</template>
<view v-else-if="canCancel(item.checkStatus)" class="action-btn cancel-btn" @tap="handleCancel(item)">取消邀约</view>
</view>
</view>
</view>
<!-- 下拉刷新和上拉加载的提示 -->
<view class="loading-more" v-if="loading">
<text>加载中...</text>
</view>
<view class="no-more" v-if="!hasMore && visitorList.length > 0">
<text>没有更多了</text>
</view>
<view class="empty" v-if="!loading && visitorList.length === 0">
<text>暂无数据</text>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import visitorApi from '@/sheep/api/integrated/visitors'
import dayjs from 'dayjs'
const tabs = [
{ label: '全部', value: '' },
{ label: '待审核', value: '1' },
{ label: '待到访', value: '2' },
{ label: '已取消', value: '5' }
]
const currentTab = ref('')
const visitorList = ref<any[]>([])
const loading = ref(false)
const hasMore = ref(true)
const current = ref(1)
const size = ref(10)
const reasonDict = ref<any[]>([])
// 获取拜访原因字典
const getReasonDict = async () => {
try {
const { code, data } = await visitorApi.getDictListByType(1)
if (code === 0 && data) {
reasonDict.value = data
}
} catch (error) {
console.error('获取拜访原因字典失败:', error)
}
}
// 获取拜访原因文本
const getReasonText = (value: string) => {
const reason = reasonDict.value.find(item => item.value === value)
return reason ? reason.label : value
}
// 格式化日期时间
const formatDateTime = (dateStr: string) => {
if (!dateStr) return ''
return dayjs(dateStr).format('YYYY/MM/DD HH:mm')
}
// 获取状态文本
const getStatusText = (status: string) => {
const statusMap: { [key: string]: string } = {
'1': '待审核',
'2': '待到访',
'5': '已取消',
'0': '已到访'
}
return statusMap[status] || ''
}
// 获取状态样式类
const getStatusClass = (status: string) => {
const statusClassMap: { [key: string]: string } = {
'1': 'status-pending',
'2': 'status-waiting',
'5': 'status-cancelled',
'0': 'status-visited'
}
return statusClassMap[status] || ''
}
// 判断是否可以取消
const canCancel = (status: string) => {
return ['1', '2'].includes(status)
}
// 处理标签切换
const handleTabChange = (value: string) => {
if (currentTab.value === value) return
currentTab.value = value
getVisitorList(true)
}
// 获取访客列表
const getVisitorList = async (isRefresh = false) => {
if (isRefresh) {
current.value = 1
hasMore.value = true
visitorList.value = []
}
if (!hasMore.value || loading.value) return
loading.value = true
try {
// 确保先获取拜访原因字典
if (reasonDict.value.length === 0) {
await getReasonDict()
}
const { code, data } = await visitorApi.getVisitorYYList({
current: current.value,
size: size.value,
checkStatus: currentTab.value || undefined
})
if (code === 0 && data) {
if (isRefresh) {
visitorList.value = data.records || []
} else {
visitorList.value = [...visitorList.value, ...(data.records || [])]
}
hasMore.value = data.records.length === size.value
if (hasMore.value) {
current.value++
}
}
} catch (error) {
console.error('获取访客列表失败:', error)
} finally {
loading.value = false
}
}
// 处理取消邀约
const handleCancel = async (item: any) => {
uni.showModal({
title: '提示',
content: '确定要取消该邀约吗?',
success: async (res) => {
if (res.confirm) {
try {
const { code, msg } = await visitorApi.cancelVisitor(item.id)
if (code === 0) {
uni.showToast({
title: '取消成功',
icon: 'success'
})
// 刷新列表
getVisitorList(true)
} else {
uni.showToast({
title: msg || '取消失败',
icon: 'none'
})
}
} catch (error) {
console.error('取消邀约失败:', error)
uni.showToast({
title: '取消失败',
icon: 'none'
})
}
}
}
})
}
// 处理接受邀约
const handleAccept = async (item: any) => {
uni.showModal({
title: '提示',
content: '确定接受该邀约吗?',
success: async (res) => {
if (res.confirm) {
try {
const { code, msg } = await visitorApi.updateCheckStatus({
id: item.id,
checkStatus: '2'
})
if (code === 0) {
uni.showToast({
title: '已接受',
icon: 'success'
})
// 刷新列表
getVisitorList(true)
} else {
uni.showToast({
title: msg || '操作失败',
icon: 'none'
})
}
} catch (error) {
console.error('接受邀约失败:', error)
uni.showToast({
title: '操作失败',
icon: 'none'
})
}
}
}
})
}
// 处理拒绝邀约
const handleReject = async (item: any) => {
uni.showModal({
title: '提示',
content: '确定拒绝该邀约吗?',
success: async (res) => {
if (res.confirm) {
try {
const { code, msg } = await visitorApi.updateCheckStatus({
id: item.id,
checkStatus: '3'
})
if (code === 0) {
uni.showToast({
title: '已拒绝',
icon: 'success'
})
// 刷新列表
getVisitorList(true)
} else {
uni.showToast({
title: msg || '操作失败',
icon: 'none'
})
}
} catch (error) {
console.error('拒绝邀约失败:', error)
uni.showToast({
title: '操作失败',
icon: 'none'
})
}
}
}
})
}
// 页面生命周期钩子
defineExpose({
async onLoad() {
await getReasonDict()
getVisitorList(true)
},
onPullDownRefresh() {
getVisitorList(true).then(() => {
uni.stopPullDownRefresh()
})
},
onReachBottom() {
getVisitorList()
}
})
// 页面配置
defineOptions({
navigationBarTitleText: '我的邀约',
enablePullDownRefresh: true,
backgroundColor: '#f5f5f5'
})
</script>
<style lang="scss" scoped>
.page-container {
min-height: 100vh;
background-color: #f5f5f5;
}
.status-tabs {
display: flex;
background-color: #fff;
padding: 20rpx 30rpx;
margin-bottom: 20rpx;
.tab-item {
flex: 1;
text-align: center;
font-size: 28rpx;
color: #666;
position: relative;
padding: 20rpx 0;
&.active {
color: #1777FF;
font-weight: 500;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 40rpx;
height: 4rpx;
background-color: #1777FF;
border-radius: 2rpx;
}
}
}
}
.visitor-list {
padding: 0 20rpx;
.visitor-item {
background-color: #fff;
border-radius: 12rpx;
margin-bottom: 20rpx;
padding: 24rpx;
.visitor-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
.visit-time {
font-size: 28rpx;
color: #333;
}
.status-tag {
padding: 4rpx 16rpx;
border-radius: 4rpx;
font-size: 24rpx;
&.status-pending {
background-color: #fff7e6;
color: #ffa940;
}
&.status-waiting {
background-color: #e6f7ff;
color: #1890ff;
}
&.status-visited {
background-color: #f6ffed;
color: #52c41a;
}
&.status-cancelled {
background-color: #f5f5f5;
color: #999;
}
}
}
.visitor-content {
.info-row {
display: flex;
margin-bottom: 16rpx;
.label {
width: 160rpx;
color: #666;
font-size: 28rpx;
}
.value {
flex: 1;
color: #333;
font-size: 28rpx;
}
}
}
.visitor-footer {
margin-top: 20rpx;
display: flex;
justify-content: flex-end;
gap: 20rpx;
.action-btn {
padding: 12rpx 24rpx;
border: 1rpx solid #ddd;
border-radius: 4rpx;
font-size: 26rpx;
&:active {
opacity: 0.8;
}
}
.cancel-btn {
color: #666;
}
.reject-btn {
color: #ff4d4f;
border-color: #ff4d4f;
}
.accept-btn {
color: #fff;
background-color: #1777FF;
border-color: #1777FF;
}
}
}
}
.loading-more, .no-more, .empty {
text-align: center;
padding: 30rpx;
color: #999;
font-size: 26rpx;
}
</style>
+2
View File
@@ -78,6 +78,8 @@ const state = reactive<{
{ text: "人脸管理", icon: "/static/icon/service_5.png", router: "/pages/human-face/list" },
{ text: "访客", icon: "/static/icon/service_5.png", router: "/pages/integrated/visitor/index" },
{ text: "访客记录", icon: "/static/icon/service_5.png", router: "/pages/integrated/visitor/myvisitor" },
{ text: "访客邀约", icon: "/static/icon/service_5.png", router: "/pages/integrated/visitor/myyyvisitor" },
{ text: "邀约记录", icon: "/static/icon/service_5.png", router: "/pages/integrated/visitor/myyyvisitorlist" },
// { text: "意见反馈", icon: "/static/icon/service_5.png", router: "/pages/integrated/feedback/index" }
],
appInfo: "",
+95
View File
@@ -22,6 +22,101 @@ const visitorApi = {
},
}),
// 获取访客记录列表
getVisitorList: (params: {
current: number;
size: number;
checkStatus?: string;
}) =>
request({
url: "/integrated/visitors/app/sq/page",
method: "GET",
params,
custom: {
showLoading: false,
},
}),
// 取消访客预约
cancelVisitor: (id: string) =>
request({
url: "/integrated/visitors/app/cancelVisitor",
method: "GET",
params: { id },
custom: {
showLoading: false,
},
}),
// 获取拜访原因字典
getDictListByType: (type: number) =>
request({
url: '/integrated/visitors/app/getDictListByType',
method: 'GET',
params: { type },
custom: {
showLoading: false,
},
}),
// 保存访客邀约
saveYY: (data: any) =>
request({
url: "/integrated/visitors/app/saveYY",
method: "POST",
data,
custom: {
showLoading: false,
},
}),
// 获取邀约记录列表
getVisitorYYList: (params: {
current: number;
size: number;
checkStatus?: string;
}) =>
request({
url: "/integrated/visitors/app/yy/page",
method: "GET",
params,
custom: {
showLoading: false,
},
}),
// 接受访客邀约
acceptVisitor: (id: string) =>
request({
url: "/integrated/visitors/app/acceptYY",
method: "GET",
params: { id },
custom: {
showLoading: false,
},
}),
// 拒绝访客邀约
rejectVisitor: (id: string) =>
request({
url: "/integrated/visitors/app/rejectYY",
method: "GET",
params: { id },
custom: {
showLoading: false,
},
}),
// 更新访客邀约状态
updateCheckStatus: (params: { id: string; checkStatus: string }) =>
request({
url: "/integrated/visitors/app/updateCheckStatus",
method: "GET",
params,
custom: {
showLoading: false,
},
}),
}
export default visitorApi