Merge branch 'fanpeng' into 'develop'
feat: 完成审批发起申请和审批中心的UI See merge request StarlockTeam/starwork-uniapp!11
This commit is contained in:
commit
b6773b4d75
@ -56,10 +56,11 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useBasicStore } from '@/store'
|
||||
import GetSystemInfoResult = UniNamespace.GetSystemInfoResult
|
||||
|
||||
const $basic = useBasicStore()
|
||||
|
||||
const systemInfo = ref(null)
|
||||
const systemInfo = ref<GetSystemInfoResult>(null)
|
||||
const show = ref<boolean>(false)
|
||||
|
||||
onMounted(async () => {
|
||||
|
||||
@ -26,14 +26,21 @@
|
||||
class="pos-absolute left-3 h-5 w-5"
|
||||
@click="back"
|
||||
></image>
|
||||
<view class="flex-1 text-center">
|
||||
<view class="line-clamp-1 max-w-60 break-all mx-auto">
|
||||
<view class="flex flex-items-center">
|
||||
<view class="line-clamp-1 max-w-60 break-all">
|
||||
{{ title }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="pos-absolute right--3">
|
||||
<slot></slot>
|
||||
</view>
|
||||
<view
|
||||
v-if="rightButtonText"
|
||||
class="text-3.5 font-normal pos-absolute right-5"
|
||||
@click="rightButton"
|
||||
>
|
||||
{{ rightButtonText }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -41,10 +48,11 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useBasicStore } from '@/store'
|
||||
import GetSystemInfoResult = UniNamespace.GetSystemInfoResult
|
||||
|
||||
const $basic = useBasicStore()
|
||||
|
||||
const systemInfo = ref(null)
|
||||
const systemInfo = ref<GetSystemInfoResult>(null)
|
||||
|
||||
defineProps({
|
||||
mini: {
|
||||
@ -62,16 +70,24 @@
|
||||
backUrl: {
|
||||
type: String,
|
||||
default: '/static/images/icon_back.png'
|
||||
},
|
||||
rightButtonText: {
|
||||
type: [String, null],
|
||||
default: null
|
||||
}
|
||||
})
|
||||
|
||||
const emits = defineEmits(['back'])
|
||||
const emits = defineEmits(['back', 'rightButton'])
|
||||
|
||||
const back = () => {
|
||||
uni.navigateBack()
|
||||
emits('back')
|
||||
}
|
||||
|
||||
const rightButton = () => {
|
||||
emits('rightButton')
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
systemInfo.value = await $basic.getSystemInfo()
|
||||
})
|
||||
|
||||
122
src/pages.json
122
src/pages.json
@ -60,8 +60,12 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/application-list/application-list",
|
||||
"type": "page"
|
||||
"path": "pages/approval/approval-detail",
|
||||
"type": "page",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/approval/approval",
|
||||
@ -71,6 +75,14 @@
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/approval/create-application",
|
||||
"type": "page",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/attendance/allowed-time",
|
||||
"type": "page",
|
||||
@ -136,22 +148,16 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/code/code",
|
||||
"type": "page",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": true
|
||||
},
|
||||
"needLogin": false
|
||||
"path": "pages/home/application-list",
|
||||
"type": "page"
|
||||
},
|
||||
{
|
||||
"path": "pages/get-code/get-code",
|
||||
"path": "pages/info-publish/add-release-plan",
|
||||
"type": "page",
|
||||
"layout": "default",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": true
|
||||
},
|
||||
"needLogin": false
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/info-publish/announce-notice",
|
||||
@ -177,9 +183,29 @@
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/info-publish/edit-pic-video",
|
||||
"type": "page",
|
||||
"layout": "default",
|
||||
"style": {
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/info-publish/info-publish",
|
||||
"type": "page"
|
||||
"type": "page",
|
||||
"layout": "default",
|
||||
"style": {
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/info-publish/material-library",
|
||||
"type": "page",
|
||||
"layout": "default",
|
||||
"style": {
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/info-publish/notice-details",
|
||||
@ -197,6 +223,40 @@
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/info-publish/play-content-library",
|
||||
"type": "page",
|
||||
"layout": "default",
|
||||
"style": {
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/info-publish/release-plan",
|
||||
"type": "page",
|
||||
"layout": "default",
|
||||
"style": {
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/code",
|
||||
"type": "page",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": true
|
||||
},
|
||||
"needLogin": false
|
||||
},
|
||||
{
|
||||
"path": "pages/login/get-code",
|
||||
"type": "page",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": true
|
||||
},
|
||||
"needLogin": false
|
||||
},
|
||||
{
|
||||
"path": "pages/login/login",
|
||||
"type": "page",
|
||||
@ -206,6 +266,23 @@
|
||||
},
|
||||
"needLogin": false
|
||||
},
|
||||
{
|
||||
"path": "pages/login/reset-password",
|
||||
"type": "page",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": true
|
||||
},
|
||||
"needLogin": false
|
||||
},
|
||||
{
|
||||
"path": "pages/mine/mine",
|
||||
"type": "page",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "首页"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/notification/notification",
|
||||
"type": "page"
|
||||
@ -219,13 +296,20 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/reset-password/reset-password",
|
||||
"path": "pages/select/select-access-control",
|
||||
"type": "page",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": true
|
||||
},
|
||||
"needLogin": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/select/select-organization",
|
||||
"type": "page",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/workbench/workbench",
|
||||
@ -281,4 +365,4 @@
|
||||
}
|
||||
],
|
||||
"subPackages": []
|
||||
}
|
||||
}
|
||||
66
src/pages/approval/approval-detail.vue
Normal file
66
src/pages/approval/approval-detail.vue
Normal file
@ -0,0 +1,66 @@
|
||||
<route lang="json5">
|
||||
{
|
||||
style: {
|
||||
navigationStyle: 'custom',
|
||||
disableScroll: true
|
||||
}
|
||||
}
|
||||
</route>
|
||||
<template>
|
||||
<view class="h-100vh flex flex-col">
|
||||
<TopNavigation title="详情"></TopNavigation>
|
||||
<scroll-view class="flex-1 box-border" :scroll-y="true">
|
||||
<view class="pb-5">
|
||||
<ApprovalContent class="mt-2" />
|
||||
<ApprovalProcess class="mt-2" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="pb-safe border-#eef0f5 border-t-solid">
|
||||
<view class="flex flex-items-center flex-justify-around py-3">
|
||||
<view class="py-2 px-13 bg-#ef2e2f color-white rounded-2">拒绝</view>
|
||||
<view class="py-2 px-13 custom-bg-blue color-white rounded-2">同意</view>
|
||||
<view class="custom-color-blue text-4" @click="showActions">更多</view>
|
||||
</view>
|
||||
</view>
|
||||
<wd-action-sheet
|
||||
v-model="show"
|
||||
:actions="actions"
|
||||
close-on-click-action
|
||||
@close="close"
|
||||
@select="select"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useToast } from 'wot-design-uni'
|
||||
import ApprovalContent from '@/pages/approval/components/ApprovalContent.vue'
|
||||
import ApprovalProcess from '@/pages/approval/components/ApprovalProcess.vue'
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
const show = ref<boolean>(false)
|
||||
const actions = ref([
|
||||
{
|
||||
name: '专审'
|
||||
},
|
||||
{
|
||||
name: '退回'
|
||||
},
|
||||
{
|
||||
name: '取消'
|
||||
}
|
||||
])
|
||||
|
||||
const showActions = () => {
|
||||
show.value = true
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
show.value = false
|
||||
}
|
||||
|
||||
const select = ({ item, index }) => {
|
||||
toast.show(`当前选中项: ${item.title}, 下标: ${index}`)
|
||||
}
|
||||
</script>
|
||||
@ -7,14 +7,20 @@
|
||||
}
|
||||
</route>
|
||||
<template>
|
||||
<view>
|
||||
<view class="h-100vh flex flex-col">
|
||||
<TopNavigation :title="index === 0 ? '发起申请' : '审批'"></TopNavigation>
|
||||
<ApplicationList v-show="index === 0" :list="applicationListData"></ApplicationList>
|
||||
<view v-show="index === 1" class="flex-1 box-border">
|
||||
<ApprovalRecords></ApprovalRecords>
|
||||
</view>
|
||||
<CustomTabBar :list="list" :default-index="index" @change="change"></CustomTabBar>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { TabBarItem } from '@/typings'
|
||||
import ApplicationList from '@/pages/approval/components/ApplicationList.vue'
|
||||
import ApprovalRecords from '@/pages/approval/components/ApprovalRecords.vue'
|
||||
|
||||
const index = ref<number>(0)
|
||||
|
||||
@ -29,6 +35,78 @@
|
||||
}
|
||||
]
|
||||
|
||||
const applicationListData = ref<Array<object>>([
|
||||
{
|
||||
groupId: 138603,
|
||||
groupName: '出勤休假',
|
||||
sort: 1,
|
||||
processDefs: [
|
||||
{
|
||||
processDefId: 249155,
|
||||
processDefName: '请假',
|
||||
icon: 'https://file.hikvisionmall.com/test1/image/f39c654cd30a48d28ddf37e4b64de57a.png',
|
||||
version: '1735128632'
|
||||
},
|
||||
{
|
||||
processDefId: 249156,
|
||||
processDefName: '补卡',
|
||||
icon: 'https://file.hikvisionmall.com/test1/image/26209b6732f2439fae3c6dd34a39145e.png',
|
||||
version: '1704439843'
|
||||
},
|
||||
{
|
||||
processDefId: 249158,
|
||||
processDefName: '外出',
|
||||
icon: 'https://file.hikvisionmall.com/test1/image/5c76288745dd47a38c9e1093d9737976.png',
|
||||
version: '1704439843'
|
||||
},
|
||||
{
|
||||
processDefId: 249157,
|
||||
processDefName: '出差',
|
||||
icon: 'https://file.hikvisionmall.com/test1/image/df75579e70c2435e9f78d04fc401b451.png',
|
||||
version: '1704439843'
|
||||
},
|
||||
{
|
||||
processDefId: 347336,
|
||||
processDefName: '加班',
|
||||
icon: 'https://file.hikvisionmall.com/test1/image/e4f21377ea2443b3856b07674ba41c2a.png',
|
||||
version: '1711368134'
|
||||
},
|
||||
{
|
||||
processDefId: 891737,
|
||||
processDefName: '调休',
|
||||
icon: 'https://file.hikvisionmall.com/prod/image/3d134cc9657e4816ad6aa8ef206fbc89.png',
|
||||
version: '1735128832'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
groupId: 341473,
|
||||
groupName: 'test',
|
||||
sort: 2,
|
||||
processDefs: [
|
||||
{
|
||||
processDefId: 924255,
|
||||
processDefName: 'test1',
|
||||
icon: 'https://file.hikvisionmall.com/prod/image/3d134cc9657e4816ad6aa8ef206fbc89.png',
|
||||
version: '1736480449'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
groupId: 138604,
|
||||
groupName: '其他',
|
||||
sort: 999,
|
||||
processDefs: [
|
||||
{
|
||||
processDefId: 466818,
|
||||
processDefName: '访客预约',
|
||||
icon: 'https://file.hikmall.com/prod/image/5300a838119d462b9e1c17489603b6d8.png',
|
||||
version: '1715675086'
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
|
||||
onLoad(options => {
|
||||
if (options.index) {
|
||||
index.value = Number(options.index)
|
||||
|
||||
65
src/pages/approval/components/ApplicationList.vue
Normal file
65
src/pages/approval/components/ApplicationList.vue
Normal file
@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<scroll-view class="flex-1 box-border" :scroll-y="true">
|
||||
<view class="mx-4 pt-4 flex flex-items-center text-3.5 font-bold">
|
||||
<view class="bg-#dfecff flex-1 px-3 py-6 rounded-2">
|
||||
<view>使用指南</view>
|
||||
</view>
|
||||
<view class="w-4"></view>
|
||||
<view class="bg-#feebda flex-1 px-3 py-6 rounded-2">
|
||||
<view>常见问题</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="py-4">
|
||||
<wd-collapse v-model="collapseRoot" custom-class="!bg-transparent !border-0">
|
||||
<wd-collapse-item
|
||||
v-for="group in list"
|
||||
:key="group.groupId"
|
||||
:name="group.groupName"
|
||||
:title="group.groupName"
|
||||
>
|
||||
<view
|
||||
v-for="(item, index) in group.processDefs"
|
||||
:key="item.processDefId"
|
||||
:class="[index === group.processDefs.length - 1 ? '' : 'mb-2']"
|
||||
class="flex flex-items-center bg-white px-3 py-3 mx-1 rounded-2"
|
||||
@click="toDetail(item)"
|
||||
>
|
||||
<image :src="item.icon" class="w-7 h-7 rounded-2"></image>
|
||||
<view class="text-3.5 ml-2">{{ item.processDefName }}</view>
|
||||
</view>
|
||||
</wd-collapse-item>
|
||||
</wd-collapse>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const collapseRoot = ref<string[]>([])
|
||||
|
||||
onMounted(() => {
|
||||
collapseRoot.value = props.list.map(item => item.groupName)
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
list: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const toDetail = (item: Record<string, any>) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/approval/create-application?id=${item.processDefId}&title=${item.processDefName}`
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wd-collapse-item::after {
|
||||
height: 0 !important;
|
||||
}
|
||||
|
||||
:deep(.wd-collapse-item__header::after) {
|
||||
height: 0 !important;
|
||||
}
|
||||
</style>
|
||||
61
src/pages/approval/components/ApprovalContent.vue
Normal file
61
src/pages/approval/components/ApprovalContent.vue
Normal file
@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<view class="bg-white rounded-2 py-2 px-4 mx-4">
|
||||
<view class="flex flex-items-center">
|
||||
<image
|
||||
src="/static/images/icon_default_avatar.png"
|
||||
class="w-10 h-10"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<view class="ml-4">
|
||||
<view class="font-bold text-4.5">请假</view>
|
||||
<view class="text-3.5 mt-1">xxx</view>
|
||||
</view>
|
||||
<view
|
||||
class="ml-a rounded py-0.5 px-1.5 text-3.5 border-solid border-1 border-#ef811c color-#ef811c"
|
||||
>
|
||||
审批中
|
||||
</view>
|
||||
</view>
|
||||
<view class="my-2 h-1px w-full bg-#eef0f5"></view>
|
||||
<view class="custom-color-black">
|
||||
<view>所属组织:</view>
|
||||
<view class="mt-2 break-all">123132123的互联</view>
|
||||
</view>
|
||||
<view class="my-2 h-1px w-full bg-#eef0f5"></view>
|
||||
<view class="custom-color-black">
|
||||
<view>审批编号:</view>
|
||||
<view class="mt-2 break-all">845465465465465465456</view>
|
||||
</view>
|
||||
<view class="my-2 h-1px w-full bg-#eef0f5"></view>
|
||||
<view class="custom-color-black">
|
||||
<view>请假类型:</view>
|
||||
<view class="mt-2 break-all">年假</view>
|
||||
</view>
|
||||
<view class="my-2 h-1px w-full bg-#eef0f5"></view>
|
||||
<view class="custom-color-black">
|
||||
<view>开始时间:</view>
|
||||
<view class="mt-2 break-all">2025-01-10</view>
|
||||
</view>
|
||||
<view class="my-2 h-1px w-full bg-#eef0f5"></view>
|
||||
<view class="custom-color-black">
|
||||
<view>结束时间:</view>
|
||||
<view class="mt-2 break-all">2025-01-10</view>
|
||||
</view>
|
||||
<view class="my-2 h-1px w-full bg-#eef0f5"></view>
|
||||
<view class="custom-color-black">
|
||||
<view>请假时长(天):</view>
|
||||
<view class="mt-2 break-all">1.0</view>
|
||||
</view>
|
||||
<view class="my-2 h-1px w-full bg-#eef0f5"></view>
|
||||
<view class="custom-color-black">
|
||||
<view>请假原因:</view>
|
||||
<view class="mt-2 break-all">
|
||||
请假请假请假请假请假请假请假请假请假请假请假请假请假请假请假请假请假请假请假请假请假请假请假请假请假请假请假1111111111111111111111
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
101
src/pages/approval/components/ApprovalCreate.vue
Normal file
101
src/pages/approval/components/ApprovalCreate.vue
Normal file
@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<view class="bg-white rounded-2 py-3 px-4 mx-4 my-2 shadow-sm">
|
||||
<view class="text-4">审批流程</view>
|
||||
<view v-if="false" class="text-3 color-#838589 mt-2">必填信息填写完整后,将显示审批流程</view>
|
||||
<view v-else class="mt-4 color-#838589">
|
||||
<wd-steps :active="3" vertical>
|
||||
<wd-step>
|
||||
<template v-slot:icon>
|
||||
<view
|
||||
class="custom-bg-blue w-5 h-5 rounded-50% flex flex-items-center flex-justify-center"
|
||||
>
|
||||
<view class="w-2.5 h-2.5 bg-white rounded-50%"></view>
|
||||
</view>
|
||||
</template>
|
||||
<template v-slot:title>
|
||||
<view class="custom-color-black text-3.5">
|
||||
<view class="flex-items-center flex">
|
||||
<view>发起人</view>
|
||||
</view>
|
||||
<view class="my-2">
|
||||
<view>
|
||||
<image
|
||||
src="https://file.hikmall.com/prod/image/4921b7596d2344088f8611f503b011dc.png"
|
||||
class="h-10 w-10 rounded-2"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<view class="mx-2">xxx</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</wd-step>
|
||||
<wd-step>
|
||||
<template v-slot:icon>
|
||||
<view class="bg-#f0801e w-5 h-5 rounded-50% flex flex-items-center flex-justify-center">
|
||||
<image src="/static/images/icon_approval.png" class="w-3 h-3"></image>
|
||||
</view>
|
||||
</template>
|
||||
<template v-slot:title>
|
||||
<view class="custom-color-black text-3.5">
|
||||
<view class="flex-items-center flex">
|
||||
<view>审批节点</view>
|
||||
</view>
|
||||
<view class="pt-2 color-#7e807f text-3">1人通过即可</view>
|
||||
<view class="my-2 flex flex-wrap">
|
||||
<view v-for="item in 8" :key="item" class="mb-2 w-fit mr-2">
|
||||
<view>
|
||||
<image
|
||||
src="https://file.hikmall.com/prod/image/4921b7596d2344088f8611f503b011dc.png"
|
||||
class="h-10 w-10 rounded-2"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<view class="mx-2">xxx</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</wd-step>
|
||||
<wd-step>
|
||||
<template v-slot:icon>
|
||||
<view class="bg-#2ab6c7 w-5 h-5 rounded-50% flex flex-items-center flex-justify-center">
|
||||
<image src="/static/images/icon_airplane.png" class="w-3 h-3"></image>
|
||||
</view>
|
||||
</template>
|
||||
<template v-slot:title>
|
||||
<view class="custom-color-black text-3.5">
|
||||
<view class="flex-items-center flex">
|
||||
<view>抄送节点</view>
|
||||
</view>
|
||||
<view class="my-2 flex flex-wrap">
|
||||
<view v-for="item in 3" :key="item" class="mb-2 w-fit mr-2">
|
||||
<view>
|
||||
<image
|
||||
src="https://file.hikmall.com/prod/image/4921b7596d2344088f8611f503b011dc.png"
|
||||
class="h-10 w-10 rounded-2"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<view class="mx-2">xxx</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</wd-step>
|
||||
</wd-steps>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
:deep(.wd-step__header) {
|
||||
width: 1.25rem;
|
||||
}
|
||||
|
||||
:deep(.wd-step__line) {
|
||||
background: #255cf7;
|
||||
}
|
||||
</style>
|
||||
81
src/pages/approval/components/ApprovalProcess.vue
Normal file
81
src/pages/approval/components/ApprovalProcess.vue
Normal file
@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<view class="bg-white rounded-2 py-2 px-4 mx-4">
|
||||
<view class="py-2 text-4">审批流程</view>
|
||||
<wd-steps :active="1" vertical>
|
||||
<wd-step icon="link">
|
||||
<template v-slot:title>
|
||||
<view class="custom-color-black text-3.5">
|
||||
<view class="flex-items-center flex">
|
||||
<view>发起申请</view>
|
||||
<view class="ml-a color-#7e807f text-3">2504-12-25 20:11</view>
|
||||
</view>
|
||||
<view class="my-2">
|
||||
<view class="py-1 flex flex-items-center w-fit rounded-4 bg-#f3f5fa">
|
||||
<image
|
||||
src="/static/images/icon_default_avatar.png"
|
||||
class="h-6 w-6 rounded-50%"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<view class="mx-2">xxx</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</wd-step>
|
||||
<wd-step icon="link">
|
||||
<template v-slot:title>
|
||||
<view class="custom-color-black text-3.5">
|
||||
<view class="flex-items-center flex">
|
||||
<view>审批节点</view>
|
||||
<view
|
||||
class="ml-3 rounded-4 py-0.5 text-3 px-2 border-solid border-#ef811c border-1 color-#ef811c"
|
||||
>
|
||||
审批中
|
||||
</view>
|
||||
</view>
|
||||
<view class="pt-2 color-#7e807f text-3">1人通过即可</view>
|
||||
<view class="my-2">
|
||||
<view
|
||||
v-for="item in 4"
|
||||
:key="item"
|
||||
class="mb-2 py-1 flex flex-items-center w-fit rounded-4 bg-#f3f5fa"
|
||||
>
|
||||
<image
|
||||
src="/static/images/icon_default_avatar.png"
|
||||
class="h-6 w-6 rounded-50%"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<view class="mx-2">xxx</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</wd-step>
|
||||
<wd-step icon="link">
|
||||
<template v-slot:title>
|
||||
<view class="custom-color-black text-3.5">
|
||||
<view class="flex-items-center flex">
|
||||
<view>抄送节点</view>
|
||||
</view>
|
||||
<view class="my-2">
|
||||
<view
|
||||
v-for="item in 4"
|
||||
:key="item"
|
||||
class="mb-2 py-1 flex flex-items-center w-fit rounded-4 bg-#f3f5fa"
|
||||
>
|
||||
<image
|
||||
src="/static/images/icon_default_avatar.png"
|
||||
class="h-6 w-6 rounded-50%"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<view class="mx-2">xxx</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</wd-step>
|
||||
</wd-steps>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
158
src/pages/approval/components/ApprovalRecords.vue
Normal file
158
src/pages/approval/components/ApprovalRecords.vue
Normal file
@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<view class="mx-4 mt-2">
|
||||
<wd-input
|
||||
class="px-2 py-0.6 rounded-1"
|
||||
placeholder="申请名称、发起人"
|
||||
v-model="search"
|
||||
clearable
|
||||
custom-class="border-0"
|
||||
:maxlength="50"
|
||||
prefix-icon="search"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</view>
|
||||
<wd-tabs v-model="currentIndex" class="pt-3" auto-line-width @click="handleChange">
|
||||
<wd-tab :title="item" v-for="(item, index) in tabs" :key="item" :name="index" />
|
||||
</wd-tabs>
|
||||
<swiper :current="currentIndex" @change="changeIndex">
|
||||
<swiper-item v-for="(item, index) in tabs" :key="item" class="flex flex-col">
|
||||
<view @click="closeOutside">
|
||||
<wd-drop-menu>
|
||||
<wd-drop-menu-item
|
||||
v-model="typeList[index]"
|
||||
:options="typeOptionList[index]"
|
||||
@change="typeChange"
|
||||
/>
|
||||
<wd-drop-menu-item
|
||||
v-model="dateList[index]"
|
||||
:options="dateOptions"
|
||||
@change="dateChange"
|
||||
/>
|
||||
</wd-drop-menu>
|
||||
</view>
|
||||
<scroll-view :scroll-y="true" class="flex-1 box-border">
|
||||
<view class="pt-2">
|
||||
<view
|
||||
v-for="item in 10"
|
||||
:key="item"
|
||||
class="mx-4 my-2 p-4 bg-white shadow-sm rounded-2 text-3.5"
|
||||
@click="toDetail(item)"
|
||||
>
|
||||
<view class="flex flex-items-center mb-2">
|
||||
<view class="text-4.5 font-bold">请假</view>
|
||||
<view
|
||||
class="ml-a px-3 py-1 border-solid border-[#2bbbc3] border-1 color-#2bbbc3 rounded"
|
||||
>
|
||||
通过
|
||||
</view>
|
||||
</view>
|
||||
<view class="mb-2">请假类型:年假</view>
|
||||
<view class="mb-2">开始时间:2025-12-31</view>
|
||||
<view class="mb-2">结束时间:2025-12-31</view>
|
||||
<view class="flex flex-items-center">
|
||||
<image class="w-8 h-8" src="/static/images/icon_default_avatar.png"></image>
|
||||
<view class="ml-4">xxx</view>
|
||||
<view class="ml-a">2025-12-25</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="py-6 mx-30%">
|
||||
<wd-divider color="#515357">已经到底了</wd-divider>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useQueue } from 'wot-design-uni'
|
||||
|
||||
const search = ref<string>('')
|
||||
|
||||
const currentIndex = ref<number>(0)
|
||||
|
||||
const tabs = ref<string[]>(['待办', '已办', '抄送我', '已发起'])
|
||||
|
||||
const { closeOutside } = useQueue()
|
||||
|
||||
const typeList = ref<Array<number>>([0, 0, 0, 0])
|
||||
const dateList = ref<Array<number>>([0, 0, 0, 0])
|
||||
|
||||
const typeOptionList = ref<Array<Array<Record<string, number>>>>([
|
||||
[
|
||||
{ label: '全部', value: 0 },
|
||||
{ label: '补卡', value: 1 },
|
||||
{ label: '请假', value: 2 }
|
||||
],
|
||||
[
|
||||
{ label: '全部', value: 0 },
|
||||
{ label: '调休', value: 1 }
|
||||
],
|
||||
[
|
||||
{ label: '全部', value: 0 },
|
||||
{ label: '补卡', value: 1 },
|
||||
{ label: '请假', value: 2 }
|
||||
],
|
||||
[
|
||||
{ label: '全部', value: 0 },
|
||||
{ label: '补卡', value: 1 },
|
||||
{ label: '请假', value: 2 }
|
||||
]
|
||||
])
|
||||
|
||||
const dateOptions = ref<Array<Record<string, number>>>([
|
||||
{ label: '全部日期', value: 0 },
|
||||
{ label: '近7日', value: 1 },
|
||||
{ label: '近14日', value: 2 },
|
||||
{ label: '近30日', value: 3 }
|
||||
])
|
||||
|
||||
const typeChange = ({ value }) => {
|
||||
console.log(value)
|
||||
}
|
||||
|
||||
const dateChange = ({ value }) => {
|
||||
console.log(value)
|
||||
}
|
||||
|
||||
const toDetail = item => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/approval/approval-detail'
|
||||
})
|
||||
}
|
||||
|
||||
const handleChange = value => {
|
||||
currentIndex.value = value.index
|
||||
}
|
||||
|
||||
const changeIndex = e => {
|
||||
currentIndex.value = e.detail.current
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wd-input::after {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.wd-drop-item {
|
||||
top: calc(var(--window-top) + 48px) !important;
|
||||
}
|
||||
|
||||
:deep(.wd-drop-menu__item.is-active .wd-drop-menu__item-title::after) {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
:deep(.wd-drop-menu__list) {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
:deep(.wd-tabs__nav-item) {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
:deep(.wd-tabs__nav-item.is-active) {
|
||||
font-weight: bold;
|
||||
color: #255cf7;
|
||||
}
|
||||
</style>
|
||||
273
src/pages/approval/components/DatetimePicker.vue
Normal file
273
src/pages/approval/components/DatetimePicker.vue
Normal file
@ -0,0 +1,273 @@
|
||||
<template>
|
||||
<view class="px-2 text-3.5">
|
||||
<view v-if="inline">
|
||||
<view class="flex flex-items-center pos-relative pt-2 pb-1">
|
||||
<view v-if="required" class="color-#f62933 pos-absolute left--2 mt-1">*</view>
|
||||
<view>{{ title }}</view>
|
||||
<view @click="openDialog" class="flex-1 flex flex-items-center">
|
||||
<view :class="[text ? 'custom-color-black' : 'color-#bfbfbf']" class="ml-a mr-2">
|
||||
{{ text || placeholder }}
|
||||
</view>
|
||||
<wd-icon name="arrow-right" color="rgba(0,0,0,0.25)" size="18px"></wd-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else>
|
||||
<view class="flex flex-items-center pos-relative">
|
||||
<view v-if="required" class="color-#f62933 pos-absolute left--2 mt-1">*</view>
|
||||
<view>{{ title }}</view>
|
||||
</view>
|
||||
<view class="py-2" @click="openDialog">
|
||||
<view class="flex flex-items-center flex-justify-between">
|
||||
<view :class="[text ? 'custom-color-black' : 'color-#bfbfbf']">
|
||||
{{ text || placeholder }}
|
||||
</view>
|
||||
<wd-icon name="arrow-right" color="rgba(0,0,0,0.25)" size="18px"></wd-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<wd-popup
|
||||
v-model="show"
|
||||
position="bottom"
|
||||
custom-style="border-radius:12rpx;"
|
||||
@after-leave="afterLeave"
|
||||
>
|
||||
<view>
|
||||
<view class="p-4 text-3.5 border-b-1 border-[#dcdcdc] border-solid flex flex-items-center">
|
||||
<view class="font-bold">日期:</view>
|
||||
<view class="font-bold ml-a" v-if="type !== 'dateHalfDay'">{{ date }}</view>
|
||||
</view>
|
||||
<view v-if="(type === 'date' || type === 'datetime') && showPicker">
|
||||
<wd-datetime-picker-view
|
||||
:formatter="formatter"
|
||||
:type="type"
|
||||
v-model="timestamp"
|
||||
label="日期选择"
|
||||
@change="change"
|
||||
/>
|
||||
</view>
|
||||
<view v-if="type === 'dateHalfDay' && showPicker">
|
||||
<wd-tabs v-model="tab">
|
||||
<wd-tab :title="index === 0 ? date : halfDay" v-for="(item, index) in 2" :key="index">
|
||||
<view v-show="index === 0">
|
||||
<wd-datetime-picker-view
|
||||
:formatter="formatter"
|
||||
type="date"
|
||||
v-model="timestamp"
|
||||
@change="change"
|
||||
/>
|
||||
</view>
|
||||
<view v-show="index === 1">
|
||||
<wd-picker-view :columns="['上午', '下午']" v-model="halfDay" @change="change" />
|
||||
</view>
|
||||
</wd-tab>
|
||||
</wd-tabs>
|
||||
</view>
|
||||
<view class="pb-safe">
|
||||
<view class="flex py-3 flex-items-center flex-justify-around text-3.5 px-2 font-bold">
|
||||
<view
|
||||
class="w-45% bg-#eeeff4 custom-color-blue py-2.5 rounded-2 text-center"
|
||||
@click="show = false"
|
||||
>
|
||||
取消
|
||||
</view>
|
||||
<view
|
||||
class="w-45% custom-bg-blue color-white py-2.5 rounded-2 text-center"
|
||||
@click="confirm"
|
||||
>
|
||||
确定
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const show = ref<boolean>(false)
|
||||
const timestamp = ref<number>(0)
|
||||
const text = ref<string>('')
|
||||
const date = ref<string>('')
|
||||
const halfDay = ref<string>('')
|
||||
const showPicker = ref<boolean>(false)
|
||||
const returnTimestamp = ref<number>(0)
|
||||
|
||||
const tab = ref<number>(0)
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'date'
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
value: {
|
||||
type: [Number, null],
|
||||
default: null
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请选择'
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabledText: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
minDate: {
|
||||
type: [Number, null],
|
||||
default: null
|
||||
},
|
||||
maxDate: {
|
||||
type: [Number, null],
|
||||
default: null
|
||||
},
|
||||
minDateText: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
maxDateText: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
inline: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const formatter = (type, value) => {
|
||||
switch (type) {
|
||||
case 'year':
|
||||
return value + '年'
|
||||
case 'month':
|
||||
return value + '月'
|
||||
case 'date':
|
||||
return value + '日'
|
||||
case 'hour':
|
||||
return value + '时'
|
||||
case 'minute':
|
||||
return value + '分'
|
||||
default:
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
const emits = defineEmits(['change'])
|
||||
|
||||
onMounted(() => {
|
||||
if (props.value) {
|
||||
timestamp.value = props.value
|
||||
if (props.type === 'date') {
|
||||
date.value = dayjs(props.value).format('YYYY-M-D')
|
||||
text.value = date.value
|
||||
} else if (props.type === 'datetime') {
|
||||
date.value = dayjs(props.value).format('YYYY-M-D HH:mm')
|
||||
text.value = date.value
|
||||
} else if (props.type === 'dateHalfDay') {
|
||||
date.value = dayjs(props.value).format('YYYY年M月D日')
|
||||
halfDay.value = dayjs(props.value).hour() < 12 ? '上午' : '下午'
|
||||
text.value = `${date.value} ${halfDay.value}`
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const change = ({ value }) => {
|
||||
if (props.type === 'date') {
|
||||
date.value = dayjs(value).format('YYYY-M-D')
|
||||
} else if (props.type === 'datetime') {
|
||||
date.value = dayjs(value).format('YYYY-M-D HH:mm')
|
||||
} else if (props.type === 'dateHalfDay') {
|
||||
if (tab.value === 0) {
|
||||
date.value = dayjs(value).format('YYYY年M月D日')
|
||||
} else {
|
||||
halfDay.value = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const openDialog = () => {
|
||||
if (props.disabled) {
|
||||
uni.showToast({
|
||||
title: props.disabledText,
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
if (!props.value) {
|
||||
if (text.value === '') {
|
||||
timestamp.value = new Date().getTime()
|
||||
} else {
|
||||
timestamp.value = returnTimestamp.value
|
||||
}
|
||||
|
||||
if (props.type === 'date') {
|
||||
date.value = dayjs(timestamp.value).format('YYYY-M-D')
|
||||
} else if (props.type === 'datetime') {
|
||||
date.value = dayjs(timestamp.value).format('YYYY-M-D HH:mm')
|
||||
} else if (props.type === 'dateHalfDay') {
|
||||
date.value = dayjs(timestamp.value).format('YYYY年M月D日')
|
||||
halfDay.value = dayjs(timestamp.value).hour() < 12 ? '上午' : '下午'
|
||||
}
|
||||
}
|
||||
showPicker.value = true
|
||||
show.value = true
|
||||
}
|
||||
|
||||
const confirm = () => {
|
||||
if (props.minDate && timestamp.value < props.minDate) {
|
||||
uni.showToast({
|
||||
title: props.minDateText,
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
if (props.maxDate && timestamp.value > props.maxDate) {
|
||||
uni.showToast({
|
||||
title: props.maxDateText,
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
show.value = false
|
||||
returnTimestamp.value = timestamp.value
|
||||
if (props.type === 'dateHalfDay') {
|
||||
text.value = `${date.value} ${halfDay.value}`
|
||||
} else {
|
||||
text.value = date.value
|
||||
}
|
||||
emits('change', { id: props.id, value: returnTimestamp.value, text: text.value })
|
||||
}
|
||||
|
||||
const afterLeave = () => {
|
||||
showPicker.value = false
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
text.value = ''
|
||||
date.value = ''
|
||||
halfDay.value = ''
|
||||
returnTimestamp.value = 0
|
||||
emits('change', { id: props.id, value: returnTimestamp.value, text: text.value })
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
clear
|
||||
})
|
||||
</script>
|
||||
110
src/pages/approval/components/DatetimePickerGroup.vue
Normal file
110
src/pages/approval/components/DatetimePickerGroup.vue
Normal file
@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<view>
|
||||
<DatetimePicker
|
||||
:id="1"
|
||||
ref="startDatetimePicker"
|
||||
title="开始时间"
|
||||
@change="changeDate"
|
||||
:max-date="endTimestamp"
|
||||
max-date-text="开始时间不能晚于结束时间"
|
||||
required
|
||||
:disabled="disabled"
|
||||
:disabled-text="disabledText"
|
||||
:type="unit"
|
||||
></DatetimePicker>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<DatetimePicker
|
||||
:id="2"
|
||||
title="结束时间"
|
||||
ref="endDatetimePicker"
|
||||
:min-date="startTimestamp"
|
||||
min-date-text="结束时间不能早于开始时间"
|
||||
@change="changeDate"
|
||||
required
|
||||
:type="unit"
|
||||
:disabled="disabled"
|
||||
:disabled-text="disabledText"
|
||||
></DatetimePicker>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<Input
|
||||
:id="3"
|
||||
ref="inputComponent"
|
||||
type="number"
|
||||
:title="`${textMap[type]}时长${unit === 'datetime' ? '(小时)' : '(天)'}`"
|
||||
required
|
||||
:readonly="updateTime"
|
||||
placeholder="自动计算"
|
||||
@change="inputChange"
|
||||
></Input>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import DatetimePicker from '@/pages/approval/components/DatetimePicker.vue'
|
||||
import Input from '@/pages/approval/components/Input.vue'
|
||||
|
||||
const textMap = {
|
||||
leave: '请假',
|
||||
businessTrip: '出差',
|
||||
goOut: '外出',
|
||||
overtime: '加班'
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
unit: {
|
||||
type: String,
|
||||
default: 'date'
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabledText: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
updateTime: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const startTimestamp = ref(0)
|
||||
const endTimestamp = ref(0)
|
||||
|
||||
const startDatetimePicker = ref(null)
|
||||
const endDatetimePicker = ref(null)
|
||||
const inputComponent = ref(null)
|
||||
|
||||
const changeDate = e => {
|
||||
if (e.id === 1) {
|
||||
startTimestamp.value = e.value
|
||||
} else {
|
||||
endTimestamp.value = e.value
|
||||
}
|
||||
}
|
||||
|
||||
const inputChange = e => {
|
||||
console.log('inputChange', e)
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
startDatetimePicker.value.clear()
|
||||
endDatetimePicker.value.clear()
|
||||
inputComponent.value.clear()
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
clear
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
18
src/pages/approval/components/Description.vue
Normal file
18
src/pages/approval/components/Description.vue
Normal file
@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<view class="px-2 text-3.5 pb-2">
|
||||
<view class="break-all">{{ title }}</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
145
src/pages/approval/components/Form.vue
Normal file
145
src/pages/approval/components/Form.vue
Normal file
@ -0,0 +1,145 @@
|
||||
<template>
|
||||
<view class="pb-3 mb-2">
|
||||
<Visitor></Visitor>
|
||||
<view class="bg-white mx-4 p-2 rounded-2 shadow-sm">
|
||||
<Input
|
||||
:id="0"
|
||||
value="单行"
|
||||
title="单行文本"
|
||||
:required="true"
|
||||
placeholder="单行文本"
|
||||
@change="inputChange"
|
||||
></Input>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<Textarea
|
||||
:id="1"
|
||||
title="多行文本"
|
||||
:required="false"
|
||||
placeholder="多行文本"
|
||||
@change="textareaChange"
|
||||
></Textarea>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<SelectPicker
|
||||
:value="1"
|
||||
:id="2"
|
||||
title="单选框"
|
||||
placeholder="单选"
|
||||
:columns="columns"
|
||||
></SelectPicker>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<SelectPicker
|
||||
:value="[0]"
|
||||
:id="3"
|
||||
title="多选框"
|
||||
placeholder="多选"
|
||||
type="checkbox"
|
||||
:columns="columns"
|
||||
></SelectPicker>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<Input
|
||||
:id="4"
|
||||
type="number"
|
||||
title="数字"
|
||||
:required="true"
|
||||
placeholder="数字"
|
||||
@change="inputChange"
|
||||
></Input>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<DatetimePicker :id="5" title="日期1" @change="changeDate"></DatetimePicker>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<DatetimePicker
|
||||
:id="6"
|
||||
title="日期2"
|
||||
type="dateHalfDay"
|
||||
@change="changeDate"
|
||||
></DatetimePicker>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<DatetimePicker
|
||||
:id="7"
|
||||
type="datetime"
|
||||
:value="1737043380000"
|
||||
title="日期3"
|
||||
@change="changeDate"
|
||||
></DatetimePicker>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<Images :id="8" title="图片"></Images>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<Description
|
||||
:id="9"
|
||||
title="图片21378912378923718982371217389712397837889713247893428793142897134728900978"
|
||||
></Description>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<NavigatorSelector :id="10" title="成员" type="member" :multiple="true"></NavigatorSelector>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<NavigatorSelector
|
||||
:id="11"
|
||||
title="部门"
|
||||
type="department"
|
||||
:multiple="true"
|
||||
></NavigatorSelector>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<Leave :id="12"></Leave>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<DatetimePickerGroup :id="13" type="businessTrip" unit="date"></DatetimePickerGroup>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<DatetimePickerGroup :id="14" type="goOut" unit="dateHalfDay"></DatetimePickerGroup>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<Overtime :id="15"></Overtime>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<DatetimePicker
|
||||
:id="16"
|
||||
type="datetime"
|
||||
required
|
||||
title="补卡时间"
|
||||
@change="changeDate"
|
||||
></DatetimePicker>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Input from '@/pages/approval/components/Input.vue'
|
||||
import Textarea from '@/pages/approval/components/Textarea.vue'
|
||||
import SelectPicker from '@/pages/approval/components/SelectPicker.vue'
|
||||
import DatetimePicker from '@/pages/approval/components/DatetimePicker.vue'
|
||||
import Images from '@/pages/approval/components/Images.vue'
|
||||
import Description from '@/pages/approval/components/Description.vue'
|
||||
import NavigatorSelector from '@/pages/approval/components/NavigatorSelector.vue'
|
||||
import Leave from '@/pages/approval/components/Leave.vue'
|
||||
import DatetimePickerGroup from '@/pages/approval/components/DatetimePickerGroup.vue'
|
||||
import Overtime from '@/pages/approval/components/Overtime.vue'
|
||||
import Visitor from '@/pages/approval/components/Visitor.vue'
|
||||
|
||||
const columns = ref<Record<number, string>>([
|
||||
{
|
||||
value: 0,
|
||||
label: '男装'
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: '奢侈品'
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: '女装'
|
||||
}
|
||||
])
|
||||
|
||||
const inputChange = e => {
|
||||
console.log('inputChange', e)
|
||||
}
|
||||
|
||||
const textareaChange = e => {
|
||||
console.log('textareaChange', e)
|
||||
}
|
||||
|
||||
const changeDate = e => {
|
||||
console.log('changeDate', e)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wd-divider {
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
</style>
|
||||
61
src/pages/approval/components/Images.vue
Normal file
61
src/pages/approval/components/Images.vue
Normal file
@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<view class="px-2 text-3.5">
|
||||
<view class="flex flex-items-center pos-relative">
|
||||
<view v-if="required" class="color-#f62933 pos-absolute left--2 mt-1">*</view>
|
||||
<view>{{ title }}</view>
|
||||
</view>
|
||||
<view class="pt-3">
|
||||
<wd-upload
|
||||
:file-list="fileList"
|
||||
multiple
|
||||
:limit="9"
|
||||
image-mode="aspectFill"
|
||||
:size-type="['original']"
|
||||
action="https://mockapi.eolink.com/zhTuw2P8c29bc981a741931bdd86eb04dc1e8fd64865cb5/upload"
|
||||
@change="handleChange"
|
||||
></wd-upload>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const fileList = ref<any[]>([])
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (props.value.length > 0) {
|
||||
fileList.value = props.value
|
||||
}
|
||||
})
|
||||
|
||||
const handleChange = ({ fileList: files }) => {
|
||||
console.log('handleChange', files)
|
||||
fileList.value = files
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
:deep(.wd-upload__evoke),
|
||||
:deep(.wd-upload__status-content),
|
||||
:deep(.wd-upload__picture) {
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
</style>
|
||||
109
src/pages/approval/components/Input.vue
Normal file
109
src/pages/approval/components/Input.vue
Normal file
@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<view class="px-2 text-3.5">
|
||||
<view v-if="inline">
|
||||
<view class="flex flex-items-center pos-relative">
|
||||
<view v-if="required" class="color-#f62933 pos-absolute left--2 mt-1">*</view>
|
||||
<view>{{ title }}</view>
|
||||
<wd-input
|
||||
class="flex-1 ml-2"
|
||||
:type="type"
|
||||
custom-class="!bg-transparent"
|
||||
:custom-input-class="inline ? 'text-right' : 'text-left'"
|
||||
:placeholder="placeholder"
|
||||
:maxlength="maxlength ?? (type === 'text' ? 30 : 15)"
|
||||
v-model="text"
|
||||
:readonly="readonly"
|
||||
@input="change(id, $event)"
|
||||
></wd-input>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else>
|
||||
<view class="flex flex-items-center pos-relative">
|
||||
<view v-if="required" class="color-#f62933 pos-absolute left--2 mt-1">*</view>
|
||||
<view>{{ title }}</view>
|
||||
</view>
|
||||
<view class="pt-2">
|
||||
<wd-input
|
||||
:type="type"
|
||||
custom-class="!bg-transparent"
|
||||
:placeholder="placeholder"
|
||||
:maxlength="maxlength ?? (type === 'text' ? 30 : 15)"
|
||||
v-model="text"
|
||||
:readonly="readonly"
|
||||
@input="change(id, $event)"
|
||||
></wd-input>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const emits = defineEmits(['change'])
|
||||
|
||||
const text = ref<string>('')
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'text'
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请输入'
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
inline: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
maxlength: {
|
||||
type: [Number, null],
|
||||
default: null
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
text.value = props.value
|
||||
})
|
||||
|
||||
const change = (id, data) => {
|
||||
emits('change', { id, value: data.value })
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
text.value = ''
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
clear
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wd-input::after {
|
||||
height: 0 !important;
|
||||
}
|
||||
|
||||
:deep(.wd-input__inner) {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
</style>
|
||||
94
src/pages/approval/components/Keyboard.vue
Normal file
94
src/pages/approval/components/Keyboard.vue
Normal file
@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<view class="px-2 text-3.5">
|
||||
<view v-if="inline">
|
||||
<view class="flex flex-items-center pos-relative py-1.5">
|
||||
<view v-if="required" class="color-#f62933 pos-absolute left--2 mt-1">*</view>
|
||||
<view>{{ title }}</view>
|
||||
<view
|
||||
class="flex-1 ml-a text-right"
|
||||
@click="toSelect"
|
||||
:class="[text ? 'custom-color-black' : 'color-#bfbfbf']"
|
||||
>
|
||||
{{ text || placeholder }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else>
|
||||
<view class="flex flex-items-center pos-relative">
|
||||
<view v-if="required" class="color-#f62933 pos-absolute left--2 mt-1">*</view>
|
||||
<view>{{ title }}</view>
|
||||
</view>
|
||||
<view class="py-2" @click="toSelect">
|
||||
<view class="flex flex-items-center flex-justify-between">
|
||||
<view :class="[text ? 'custom-color-black' : 'color-#bfbfbf']">
|
||||
{{ text || placeholder }}
|
||||
</view>
|
||||
<wd-icon name="arrow-right" color="rgba(0,0,0,0.25)" size="18px"></wd-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<wd-keyboard
|
||||
:mode="type"
|
||||
title="键盘"
|
||||
:hide-on-click-outside="true"
|
||||
v-model:visible="visible"
|
||||
@input="onInput"
|
||||
closeText="确认"
|
||||
@delete="onDelete"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const text = ref<string>('')
|
||||
const visible = ref<boolean>(false)
|
||||
|
||||
const onInput = value => {
|
||||
if (props.maxlength && text.value.length >= props.maxlength) return
|
||||
text.value += value
|
||||
}
|
||||
const onDelete = () => {
|
||||
text.value = text.value.slice(0, -1)
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
value: {
|
||||
type: [Number, null],
|
||||
default: null
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请输入'
|
||||
},
|
||||
maxlength: {
|
||||
type: [Number, null],
|
||||
default: null
|
||||
},
|
||||
inline: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const toSelect = () => {
|
||||
visible.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
97
src/pages/approval/components/Leave.vue
Normal file
97
src/pages/approval/components/Leave.vue
Normal file
@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<view>
|
||||
<SelectPicker
|
||||
:id="0"
|
||||
title="请假类型"
|
||||
placeholder="请选择"
|
||||
:columns="columns"
|
||||
required
|
||||
@change="changeSelect"
|
||||
></SelectPicker>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<DatetimePickerGroup
|
||||
ref="timeGroup"
|
||||
type="leave"
|
||||
:disabled="!leaveType"
|
||||
:updateTime="updateTime"
|
||||
:unit="leaveType?.unit ?? 'date'"
|
||||
disabled-text="请选择请假类型"
|
||||
:id="1"
|
||||
></DatetimePickerGroup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import SelectPicker from '@/pages/approval/components/SelectPicker.vue'
|
||||
import DatetimePickerGroup from '@/pages/approval/components/DatetimePickerGroup.vue'
|
||||
|
||||
const leaveType = ref(null)
|
||||
const timeGroup = ref(null)
|
||||
|
||||
const columns = ref<Record<number, string>>([
|
||||
{
|
||||
value: 0,
|
||||
label: '年假',
|
||||
unit: 'date'
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: '事假',
|
||||
unit: 'dateHalfDay'
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: '病假',
|
||||
unit: 'datetime'
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: '婚假',
|
||||
unit: 'date',
|
||||
limit: 5
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: '产假',
|
||||
unit: 'datetime',
|
||||
limit: 24.5
|
||||
},
|
||||
{
|
||||
value: 5,
|
||||
label: '陪产假',
|
||||
unit: 'datetime'
|
||||
},
|
||||
{
|
||||
value: 6,
|
||||
label: '丧假',
|
||||
unit: 'date'
|
||||
},
|
||||
{
|
||||
value: 7,
|
||||
label: '哺乳假',
|
||||
unit: 'date'
|
||||
}
|
||||
])
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
updateTime: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const changeSelect = e => {
|
||||
leaveType.value = e.value
|
||||
timeGroup.value.clear()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wd-divider {
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
</style>
|
||||
94
src/pages/approval/components/NavigatorSelector.vue
Normal file
94
src/pages/approval/components/NavigatorSelector.vue
Normal file
@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<view class="px-2 text-3.5">
|
||||
<view v-if="inline">
|
||||
<view class="flex flex-items-center pos-relative pb-1.5" @click="toSelect">
|
||||
<view v-if="required" class="color-#f62933 pos-absolute left--2 mt-1">*</view>
|
||||
<view>{{ title }}</view>
|
||||
<view class="ml-a mr-2" :class="[text ? 'custom-color-black' : 'color-#bfbfbf']">
|
||||
{{ text || placeholder }}
|
||||
</view>
|
||||
<wd-icon name="arrow-right" color="rgba(0,0,0,0.25)" size="18px"></wd-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else>
|
||||
<view class="flex flex-items-center pos-relative">
|
||||
<view v-if="required" class="color-#f62933 pos-absolute left--2 mt-1">*</view>
|
||||
<view>{{ title }}</view>
|
||||
</view>
|
||||
<view class="py-2" @click="toSelect">
|
||||
<view class="flex flex-items-center flex-justify-between">
|
||||
<view :class="[text ? 'custom-color-black' : 'color-#bfbfbf']">
|
||||
{{ text || placeholder }}
|
||||
</view>
|
||||
<wd-icon name="arrow-right" color="rgba(0,0,0,0.25)" size="18px"></wd-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const text = ref<string | null>(null)
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
value: {
|
||||
type: [Number, null],
|
||||
default: null
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请选择'
|
||||
},
|
||||
inline: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const toSelect = () => {
|
||||
if (props.type === 'accessControl') {
|
||||
uni.navigateTo({
|
||||
url: `/pages/select/select-access-control`,
|
||||
events: {
|
||||
change: res => {
|
||||
console.log(1111, res)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const params = {
|
||||
type: props.type,
|
||||
multiple: props.multiple,
|
||||
title: props.type === 'member' ? '选择审批用户' : '选择部门'
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pages/select/select-organization?params=${JSON.stringify(params)}`,
|
||||
events: {
|
||||
change: res => {
|
||||
console.log(1111, res)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
43
src/pages/approval/components/Overtime.vue
Normal file
43
src/pages/approval/components/Overtime.vue
Normal file
@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<view>
|
||||
<DatetimePicker
|
||||
:id="0"
|
||||
ref="startDatetimePicker"
|
||||
title="加班日期"
|
||||
@change="changeDate"
|
||||
required
|
||||
></DatetimePicker>
|
||||
<wd-divider v-if="timestamp" color="#bcbfbe"></wd-divider>
|
||||
<DatetimePickerGroup
|
||||
v-if="timestamp"
|
||||
type="overtime"
|
||||
unit="datetime"
|
||||
:id="1"
|
||||
></DatetimePickerGroup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import DatetimePickerGroup from '@/pages/approval/components/DatetimePickerGroup.vue'
|
||||
import DatetimePicker from '@/pages/approval/components/DatetimePicker.vue'
|
||||
|
||||
const timestamp = ref<number>(0)
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const changeDate = e => {
|
||||
timestamp.value = e.value
|
||||
console.log('changeDate', e)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.wd-divider {
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
</style>
|
||||
128
src/pages/approval/components/SelectPicker.vue
Normal file
128
src/pages/approval/components/SelectPicker.vue
Normal file
@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<view class="px-2 text-3.5">
|
||||
<view v-if="inline">
|
||||
<view class="flex flex-items-center pos-relative">
|
||||
<view v-if="required" class="color-#f62933 pos-absolute left--2 mt-1">*</view>
|
||||
<view>{{ title }}</view>
|
||||
<wd-select-picker
|
||||
class="flex-1"
|
||||
align-right
|
||||
:type="type"
|
||||
:title="placeholder"
|
||||
:show-confirm="type === 'checkbox'"
|
||||
v-model="picker"
|
||||
:columns="columns"
|
||||
size="large"
|
||||
@change="change"
|
||||
select-size="large"
|
||||
></wd-select-picker>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else>
|
||||
<view class="flex flex-items-center pos-relative">
|
||||
<view v-if="required" class="color-#f62933 pos-absolute left--2 mt-1">*</view>
|
||||
<view>{{ title }}</view>
|
||||
</view>
|
||||
<view class="pt-2">
|
||||
<wd-select-picker
|
||||
:type="type"
|
||||
:title="placeholder"
|
||||
:show-confirm="type === 'checkbox'"
|
||||
v-model="picker"
|
||||
:columns="columns"
|
||||
size="large"
|
||||
@change="change"
|
||||
select-size="large"
|
||||
></wd-select-picker>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const picker = ref(-1)
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'radio'
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请输入'
|
||||
},
|
||||
value: {
|
||||
type: [Number, Array, null],
|
||||
default: null
|
||||
},
|
||||
columns: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
inline: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emits = defineEmits(['change'])
|
||||
|
||||
onMounted(() => {
|
||||
if (props.value) {
|
||||
picker.value = props.value
|
||||
}
|
||||
})
|
||||
|
||||
const change = ({ value }) => {
|
||||
emits('change', { id: props.id, value: props.columns.find(item => item.value === value) })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
:deep(.wd-select-picker__cell) {
|
||||
padding: var(--wot-cell-wrapper-padding, 10px) 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
:deep(.wd-radio-group),
|
||||
:deep(.wd-checkbox-group) {
|
||||
background-color: #ffffff;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
:deep(.wd-action-sheet) {
|
||||
background-color: #f3f5fa;
|
||||
}
|
||||
|
||||
:deep(.uni-scroll-view-content) {
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
:deep(.wd-radio),
|
||||
:deep(.wd-checkbox) {
|
||||
position: relative;
|
||||
padding: 1rem !important;
|
||||
}
|
||||
|
||||
:deep(.wd-radio)::after,
|
||||
:deep(.wd-checkbox)::after {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 4%;
|
||||
width: 92%;
|
||||
content: '';
|
||||
border-bottom: 1px solid #efefef;
|
||||
}
|
||||
</style>
|
||||
71
src/pages/approval/components/Textarea.vue
Normal file
71
src/pages/approval/components/Textarea.vue
Normal file
@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<view class="px-2 text-3.5">
|
||||
<view class="flex flex-items-center pos-relative">
|
||||
<view v-if="required" class="color-#f62933 pos-absolute left--2 mt-1">*</view>
|
||||
<view>{{ title }}</view>
|
||||
</view>
|
||||
<view class="pt-2">
|
||||
<wd-textarea
|
||||
custom-class="!bg-transparent"
|
||||
custom-textarea-class="h-16"
|
||||
:placeholder="placeholder"
|
||||
:maxlength="250"
|
||||
v-model="text"
|
||||
show-word-limit
|
||||
@input="change(id, $event)"
|
||||
></wd-textarea>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const emits = defineEmits(['change'])
|
||||
|
||||
const text = ref<string>('')
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请输入'
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
text.value = props.value
|
||||
})
|
||||
|
||||
const change = (id, data) => {
|
||||
emits('change', { id, value: data.value })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.wd-textarea__value),
|
||||
:deep(.wd-textarea__count) {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
:deep(.wd-textarea__inner) {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.wd-textarea {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
194
src/pages/approval/components/VisitDuration.vue
Normal file
194
src/pages/approval/components/VisitDuration.vue
Normal file
@ -0,0 +1,194 @@
|
||||
<template>
|
||||
<view class="px-2 text-3.5">
|
||||
<view v-if="inline">
|
||||
<view class="flex flex-items-center pos-relative pb-1.5" @click="toSelect">
|
||||
<view v-if="required" class="color-#f62933 pos-absolute left--2 mt-1">*</view>
|
||||
<view>{{ title }}</view>
|
||||
<view class="ml-a mr-2" :class="[text ? 'custom-color-black' : 'color-#bfbfbf']">
|
||||
{{ text || placeholder }}
|
||||
</view>
|
||||
<wd-icon name="arrow-right" color="rgba(0,0,0,0.25)" size="18px"></wd-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else>
|
||||
<view class="flex flex-items-center pos-relative">
|
||||
<view v-if="required" class="color-#f62933 pos-absolute left--2 mt-1">*</view>
|
||||
<view>{{ title }}</view>
|
||||
</view>
|
||||
<view class="py-2" @click="toSelect">
|
||||
<view class="flex flex-items-center flex-justify-between">
|
||||
<view :class="[text ? 'custom-color-black' : 'color-#bfbfbf']">
|
||||
{{ text || placeholder }}
|
||||
</view>
|
||||
<wd-icon name="arrow-right" color="rgba(0,0,0,0.25)" size="18px"></wd-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<wd-popup v-model="show" position="bottom" custom-style="border-radius:12rpx;">
|
||||
<view v-if="showDuration">
|
||||
<view
|
||||
class="px-4 py-2 text-3.5 border-b-1 border-[#dcdcdc] border-solid flex flex-items-center"
|
||||
>
|
||||
<view @click="show = false">取消</view>
|
||||
<view class="font-bold text-4 flex-1 text-center">访问时长</view>
|
||||
<view class="flex flex-items-center" @click="changeSelect">
|
||||
<view class="custom-color-blue">自定义时间</view>
|
||||
<wd-icon name="arrow-right" size="14px" class="ml-1" color="#255cf7"></wd-icon>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view class="h-60 text-3.5" :scroll-y="true">
|
||||
<view
|
||||
v-for="item in durationList"
|
||||
:key="item.value"
|
||||
@click="changeDuration(item)"
|
||||
class="py-4 px-4 flex flex-items-center"
|
||||
>
|
||||
<view>{{ item.label }}</view>
|
||||
<view
|
||||
class="bg-#f3f5fa ml-a w-12 h-6 rounded-5 flex flex-items-center flex-justify-center"
|
||||
>
|
||||
<wd-icon
|
||||
v-if="duration === item.label"
|
||||
name="check"
|
||||
size="15px"
|
||||
class="ml-1"
|
||||
color="#255cf7"
|
||||
></wd-icon>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view v-else>
|
||||
<view
|
||||
class="px-4 py-2 text-3.5 border-b-1 border-[#dcdcdc] border-solid flex flex-items-center"
|
||||
>
|
||||
<view class="flex flex-items-center" @click="showDuration = true">
|
||||
<wd-icon name="arrow-left" size="14px" class="ml-1" color="#0d0f10"></wd-icon>
|
||||
<view class="custom-color-black">返回</view>
|
||||
</view>
|
||||
<view class="font-bold text-4 flex-1 text-center">时间选择器</view>
|
||||
<wd-icon
|
||||
name="close"
|
||||
size="14px"
|
||||
class="ml-1"
|
||||
color="#0d0f10"
|
||||
@click="show = false"
|
||||
></wd-icon>
|
||||
</view>
|
||||
<wd-datetime-picker-view
|
||||
:formatter="formatter"
|
||||
type="datetime"
|
||||
v-model="timestamp"
|
||||
@change="change"
|
||||
/>
|
||||
</view>
|
||||
<view
|
||||
@click="confirm"
|
||||
class="py-2.5 rounded custom-bg-blue w-90% mx-5% font-bold text-center color-white text-3.5 my-2"
|
||||
>
|
||||
完成
|
||||
</view>
|
||||
</wd-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const text = ref<string | null>(null)
|
||||
const show = ref<boolean>(false)
|
||||
const timestamp = ref<number>(0)
|
||||
const duration = ref<string>('')
|
||||
|
||||
const showDuration = ref<boolean>(true)
|
||||
|
||||
const formatter = (type, value) => {
|
||||
switch (type) {
|
||||
case 'year':
|
||||
return value + '年'
|
||||
case 'month':
|
||||
return value + '月'
|
||||
case 'date':
|
||||
return value + '日'
|
||||
case 'hour':
|
||||
return value + '时'
|
||||
case 'minute':
|
||||
return value + '分'
|
||||
default:
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
value: {
|
||||
type: [String, null],
|
||||
default: null
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请选择'
|
||||
},
|
||||
inline: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const durationList = [
|
||||
{ label: '30分钟', value: 1800000 },
|
||||
{ label: '1小时', value: 3600000 },
|
||||
{ label: '2小时', value: 7200000 },
|
||||
{ label: '3小时', value: 10800000 },
|
||||
{ label: '5小时', value: 18000000 },
|
||||
{ label: '8小时', value: 28800000 },
|
||||
{ label: '12小时', value: 43200000 },
|
||||
{ label: '24小时', value: 86400000 }
|
||||
]
|
||||
|
||||
onMounted(() => {
|
||||
if (props.value) {
|
||||
text.value = props.value
|
||||
duration.value = props.value
|
||||
}
|
||||
})
|
||||
|
||||
const changeSelect = () => {
|
||||
timestamp.value = new Date().getTime()
|
||||
showDuration.value = false
|
||||
}
|
||||
|
||||
const toSelect = () => {
|
||||
show.value = true
|
||||
}
|
||||
|
||||
const change = value => {
|
||||
timestamp.value = value.value
|
||||
duration.value = ''
|
||||
}
|
||||
|
||||
const changeDuration = item => {
|
||||
duration.value = item.label
|
||||
timestamp.value = 0
|
||||
}
|
||||
|
||||
const confirm = () => {
|
||||
if (timestamp.value === 0) {
|
||||
text.value = duration.value
|
||||
} else {
|
||||
text.value = dayjs(timestamp.value).format('YYYY-M-D HH:mm')
|
||||
}
|
||||
show.value = false
|
||||
}
|
||||
</script>
|
||||
89
src/pages/approval/components/Visitor.vue
Normal file
89
src/pages/approval/components/Visitor.vue
Normal file
@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<view class="bg-white mx-4 p-2 rounded-2 mb-2 shadow-sm">
|
||||
<Input :id="0" title="访客姓名" :required="true" @change="inputChange" inline></Input>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<Input
|
||||
:id="1"
|
||||
title="访客手机"
|
||||
:required="true"
|
||||
:maxlength="11"
|
||||
@change="inputChange"
|
||||
inline
|
||||
type="number"
|
||||
></Input>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<SelectPicker required :id="2" title="访客类型" :columns="columns" inline></SelectPicker>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<NavigatorSelector :id="3" title="通行门禁点" type="accessControl" inline></NavigatorSelector>
|
||||
</view>
|
||||
<view class="bg-white mx-4 p-2 rounded-2 mb-2 shadow-sm">
|
||||
<Keyboard :id="4" title="车牌号码" type="car" :maxlength="8" inline></Keyboard>
|
||||
</view>
|
||||
<view class="bg-white mx-4 p-2 rounded-2 mb-2 shadow-sm">
|
||||
<DatetimePicker
|
||||
:id="5"
|
||||
ref="startDatetimePicker"
|
||||
title="预约到访时间"
|
||||
@change="changeDate"
|
||||
type="datetime"
|
||||
inline
|
||||
required
|
||||
></DatetimePicker>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<VisitDuration :id="6" title="访问时长" required value="2小时" inline></VisitDuration>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<Input :id="7" title="同行人数" @change="inputChange" inline></Input>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<Input :id="8" title="访问单位" @change="inputChange" inline></Input>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<Input :id="9" title="到访事由说明" @change="inputChange" inline></Input>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<Input :id="10" title="访问地址" @change="inputChange" inline></Input>
|
||||
<wd-divider color="#bcbfbe"></wd-divider>
|
||||
<Description :id="11" title="表单通过后,访客将收到断音邀请通知"></Description>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Input from '@/pages/approval/components/Input.vue'
|
||||
import SelectPicker from '@/pages/approval/components/SelectPicker.vue'
|
||||
import Description from '@/pages/approval/components/Description.vue'
|
||||
import Keyboard from '@/pages/approval/components/Keyboard.vue'
|
||||
import DatetimePicker from '@/pages/approval/components/DatetimePicker.vue'
|
||||
import NavigatorSelector from '@/pages/approval/components/NavigatorSelector.vue'
|
||||
import VisitDuration from '@/pages/approval/components/VisitDuration.vue'
|
||||
|
||||
const columns = ref<Record<number, string>>([
|
||||
{
|
||||
value: 0,
|
||||
label: '外卖快递'
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: '会议洽谈'
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: '探亲访友'
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: '业务沟通'
|
||||
}
|
||||
])
|
||||
|
||||
const inputChange = e => {
|
||||
console.log('inputChange', e)
|
||||
}
|
||||
|
||||
const changeDate = e => {
|
||||
// timestamp.value = e.value
|
||||
console.log('changeDate', e)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wd-divider {
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
</style>
|
||||
42
src/pages/approval/create-application.vue
Normal file
42
src/pages/approval/create-application.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<route lang="json5">
|
||||
{
|
||||
style: {
|
||||
navigationStyle: 'custom',
|
||||
disableScroll: true
|
||||
}
|
||||
}
|
||||
</route>
|
||||
<template>
|
||||
<view class="h-100vh flex flex-col">
|
||||
<TopNavigation :title="title"></TopNavigation>
|
||||
<scroll-view class="flex-1 box-border" :scroll-y="true">
|
||||
<Form></Form>
|
||||
<ApprovalCreate></ApprovalCreate>
|
||||
</scroll-view>
|
||||
<view class="pb-safe border-#eef0f5 border-t-solid">
|
||||
<view class="flex flex-items-center flex-justify-around py-3 px-3">
|
||||
<view class="py-2 w-45% text-center bg-#e2e5ea custom-color-blue rounded-2">保存草稿</view>
|
||||
<view class="opacity-60% py-2 w-45% text-center custom-bg-blue color-white rounded-2">
|
||||
提交
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Form from '@/pages/approval/components/Form.vue'
|
||||
import ApprovalCreate from '@/pages/approval/components/ApprovalCreate.vue'
|
||||
|
||||
const title = ref<string>('')
|
||||
const id = ref<number>(null)
|
||||
|
||||
onLoad(options => {
|
||||
if (options.title) {
|
||||
title.value = options.title
|
||||
}
|
||||
if (options.id) {
|
||||
id.value = Number(options.id)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@ -11,9 +11,9 @@
|
||||
<view class="h-[calc(100vh-50px)] flex flex-col">
|
||||
<TopNavigation :title="titleTab[curIndex]"></TopNavigation>
|
||||
<scroll-view class="flex-1 box-border" scroll-y>
|
||||
<view v-for="(item, index) in pages" :key="index" v-show="curIndex == index">
|
||||
<component :is="item"></component>
|
||||
</view>
|
||||
<AttendanceClockIn v-show="curIndex === 0"></AttendanceClockIn>
|
||||
<AttendanceStatistics v-show="curIndex === 1"></AttendanceStatistics>
|
||||
<AttendanceSet v-show="curIndex === 2"></AttendanceSet>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<CustomTabBar :list="list" :default-index="0" @change="change"></CustomTabBar>
|
||||
|
||||
@ -50,7 +50,7 @@
|
||||
|
||||
const clickMenu = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/application-list/application-list'
|
||||
url: '/pages/home/application-list'
|
||||
})
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
<HomeSwiper @colorChange="colorChange"></HomeSwiper>
|
||||
<HomeBulletin class="my-2"></HomeBulletin>
|
||||
<HomeTodo class="my-2"></HomeTodo>
|
||||
<CustomTab :list="featuresList" @clickItem="clickItem"></CustomTab>
|
||||
<CustomTab :list="featuresList"></CustomTab>
|
||||
<HomeOpenDoor class="my-2"></HomeOpenDoor>
|
||||
<HomeAttendance class="my-2"></HomeAttendance>
|
||||
<HomeTeamManager class="my-2"></HomeTeamManager>
|
||||
@ -56,25 +56,26 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import CustomTab from '@/pages/home/HomeTab.vue'
|
||||
import CustomTab from '@/pages/home/compoents/HomeTab.vue'
|
||||
import { useBasicStore, useUserStore } from '@/store'
|
||||
import { Result } from '@/constants/result'
|
||||
import { HomeTab } from '@/typings'
|
||||
import HomeSwiper from '@/pages/home/HomeSwiper.vue'
|
||||
import HomeBulletin from '@/pages/home/HomeBulletin.vue'
|
||||
import HomeTodo from '@/pages/home/HomeTodo.vue'
|
||||
import HomeOpenDoor from '@/pages/home/HomeOpenDoor.vue'
|
||||
import HomeAttendance from '@/pages/home/HomeAttendance.vue'
|
||||
import HomeTeamManager from '@/pages/home/HomeTeamManager.vue'
|
||||
import HomeSetting from '@/pages/home/HomeSetting.vue'
|
||||
import HomeAddDevice from '@/pages/home/HomeAddDevice.vue'
|
||||
import HomeAddTeamManager from '@/pages/home/HomeAddTeamManager.vue'
|
||||
import HomeSwiper from '@/pages/home/compoents/HomeSwiper.vue'
|
||||
import HomeBulletin from '@/pages/home/compoents/HomeBulletin.vue'
|
||||
import HomeTodo from '@/pages/home/compoents/HomeTodo.vue'
|
||||
import HomeOpenDoor from '@/pages/home/compoents/HomeOpenDoor.vue'
|
||||
import HomeAttendance from '@/pages/home/compoents/HomeAttendance.vue'
|
||||
import HomeTeamManager from '@/pages/home/compoents/HomeTeamManager.vue'
|
||||
import HomeSetting from '@/pages/home/compoents/HomeSetting.vue'
|
||||
import HomeAddDevice from '@/pages/home/compoents/HomeAddDevice.vue'
|
||||
import HomeAddTeamManager from '@/pages/home/compoents/HomeAddTeamManager.vue'
|
||||
import TeamPopup from '@/components/TeamPopup/TeamPopup.vue'
|
||||
import GetSystemInfoResult = UniNamespace.GetSystemInfoResult
|
||||
|
||||
const $user = useUserStore()
|
||||
const $basic = useBasicStore()
|
||||
|
||||
const systemInfo = ref(null)
|
||||
const systemInfo = ref<GetSystemInfoResult>(null)
|
||||
|
||||
const color = ref<string>('#bfcbef')
|
||||
|
||||
@ -318,11 +319,4 @@
|
||||
const colorChange = data => {
|
||||
color.value = data
|
||||
}
|
||||
|
||||
const clickItem = item => {
|
||||
console.log(item)
|
||||
uni.navigateTo({
|
||||
url: item.url
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -124,7 +124,7 @@
|
||||
}
|
||||
} else if (type.value === 'reset-password') {
|
||||
await uni.redirectTo({
|
||||
url: `/pages/reset-password/reset-password?phone=${phone.value}&code=${code.value}`
|
||||
url: `/pages/login/reset-password?phone=${phone.value}&code=${code.value}`
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -159,4 +159,8 @@
|
||||
border: #b8b8b8 solid 2rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
page {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
@ -63,7 +63,7 @@
|
||||
uni.hideLoading()
|
||||
if (result.errorCode === Result.Success.code) {
|
||||
await uni.navigateTo({
|
||||
url: `/pages/reset-password/reset-password?phone=${phone.value}`
|
||||
url: `/pages/login/reset-password?phone=${phone.value}`
|
||||
})
|
||||
} else {
|
||||
await uni.showToast({
|
||||
@ -85,3 +85,9 @@
|
||||
phone.value = value
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
@ -61,7 +61,7 @@
|
||||
:disabled="!phonePass"
|
||||
:round="false"
|
||||
size="large"
|
||||
@click="codeLogin"
|
||||
@click="codeLogin(false, $event)"
|
||||
>
|
||||
获取验证码
|
||||
</wd-button>
|
||||
@ -264,7 +264,7 @@
|
||||
})
|
||||
uni.hideLoading()
|
||||
if (result.errorCode === Result.Success.code) {
|
||||
let url = `/pages/code/code?phone=${phone.value}&type=login`
|
||||
let url = `/pages/login/code?phone=${phone.value}&type=login`
|
||||
if (tip) {
|
||||
url = url + `&tip=true`
|
||||
}
|
||||
@ -298,7 +298,7 @@
|
||||
params = `?phone=${phone.value}`
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pages/get-code/get-code${params}`
|
||||
url: `/pages/login/get-code${params}`
|
||||
})
|
||||
}
|
||||
|
||||
@ -325,3 +325,9 @@
|
||||
consentAgreement.value = !consentAgreement.value
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -226,4 +226,8 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
@ -1,4 +1,4 @@
|
||||
<route lang="json5" type="home">
|
||||
<route lang="json5">
|
||||
{
|
||||
style: {
|
||||
navigationStyle: 'custom',
|
||||
|
||||
56
src/pages/select/components/OrganizationItem.vue
Normal file
56
src/pages/select/components/OrganizationItem.vue
Normal file
@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<view class="flex flex-items-center py-2 text-4" @click="toClick">
|
||||
<view
|
||||
v-if="item.type === 'member'"
|
||||
class="w-10 h-10 flex flex-justify-center flex-items-center"
|
||||
>
|
||||
<image
|
||||
src="/static/images/icon_default_avatar.png"
|
||||
class="w-8 h-8 rounded-2"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
</view>
|
||||
<view v-else class="w-10 h-10 flex flex-justify-center flex-items-center bg-#e4f1ff rounded-2">
|
||||
<image src="/static/images/icon_file.png" class="w-6 h-6" mode="aspectFill"></image>
|
||||
</view>
|
||||
<view class="ml-2 break-all max-w-40 overflow-hidden text-ellipsis line-clamp-1">
|
||||
{{ item.name }}
|
||||
</view>
|
||||
<view class="ml-a" v-if="item.type !== 'member'">
|
||||
<wd-divider vertical></wd-divider>
|
||||
</view>
|
||||
<view
|
||||
v-if="item.type !== 'member'"
|
||||
:class="[disabled ? 'color-#87a9fb' : 'custom-color-blue']"
|
||||
class="ml-1"
|
||||
>
|
||||
下级
|
||||
</view>
|
||||
<wd-icon
|
||||
name="arrow-right"
|
||||
size="14px"
|
||||
class="ml-2"
|
||||
:color="disabled ? '#87a9fb' : '#255cf7'"
|
||||
v-if="item.type !== 'member'"
|
||||
></wd-icon>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const emits = defineEmits(['click'])
|
||||
|
||||
const props = defineProps({
|
||||
item: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const toClick = () => {
|
||||
emits('click')
|
||||
}
|
||||
</script>
|
||||
42
src/pages/select/components/SearchItem.vue
Normal file
42
src/pages/select/components/SearchItem.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<view class="flex flex-items-center py-2 text-4" @click="toClick">
|
||||
<view
|
||||
v-if="item.type === 'member'"
|
||||
class="w-10 h-10 flex flex-justify-center flex-items-center"
|
||||
>
|
||||
<image
|
||||
src="/static/images/icon_default_avatar.png"
|
||||
class="w-8 h-8 rounded-2"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
</view>
|
||||
<view v-else class="w-10 h-10 flex flex-justify-center flex-items-center bg-#e4f1ff rounded-2">
|
||||
<image src="/static/images/icon_file.png" class="w-6 h-6" mode="aspectFill"></image>
|
||||
</view>
|
||||
<view class="ml-2">
|
||||
<view class="break-all max-w-40 overflow-hidden text-ellipsis line-clamp-1">
|
||||
{{ item.name }}
|
||||
</view>
|
||||
<view
|
||||
class="color-#838589 text-3 break-all max-w-40 overflow-hidden text-ellipsis line-clamp-1"
|
||||
>
|
||||
xxx的互联/test
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const emits = defineEmits(['click'])
|
||||
|
||||
const props = defineProps({
|
||||
item: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const toClick = () => {
|
||||
emits('click')
|
||||
}
|
||||
</script>
|
||||
100
src/pages/select/select-access-control.vue
Normal file
100
src/pages/select/select-access-control.vue
Normal file
@ -0,0 +1,100 @@
|
||||
<route lang="json5">
|
||||
{
|
||||
style: {
|
||||
navigationStyle: 'custom',
|
||||
disableScroll: true
|
||||
}
|
||||
}
|
||||
</route>
|
||||
<template>
|
||||
<view class="h-100vh flex flex-col">
|
||||
<TopNavigation class="bg-white" title="选择门禁点"></TopNavigation>
|
||||
<scroll-view class="flex-1 box-border text-3.5 custom-color-black border-1" :scroll-y="true">
|
||||
<wd-checkbox-group v-model="value" size="large" @change="change">
|
||||
<view class="px-4 py-1.5 bg-white mb-2">
|
||||
<wd-checkbox :modelValue="item.id" v-for="item in permissionList" :key="item.id">
|
||||
<view class="flex flex-items-center">
|
||||
<view
|
||||
class="w-10 h-10 flex flex-justify-center flex-items-center bg-#e4f1ff rounded-2"
|
||||
>
|
||||
<image src="/static/images/icon_file.png" class="w-6 h-6" mode="aspectFill"></image>
|
||||
</view>
|
||||
<view class="ml-2">{{ item.name }}</view>
|
||||
</view>
|
||||
</wd-checkbox>
|
||||
</view>
|
||||
<view
|
||||
v-for="(item, index) in list"
|
||||
:key="item.id"
|
||||
class="bg-white border-#eef0f5 border-b-solid"
|
||||
:class="[index === list.length - 1 ? 'border-b-0' : 'border-b-1']"
|
||||
>
|
||||
<wd-checkbox :modelValue="item.id" class="py-1.5 px-4">
|
||||
<view class="flex flex-items-center">
|
||||
<image
|
||||
src="https://file.hikmall.com/prod/image/69c3851d052e493ebb6e7c2e7a189b61.png"
|
||||
class="w-10 h-10"
|
||||
></image>
|
||||
<view class="ml-2">{{ item.name }}</view>
|
||||
</view>
|
||||
</wd-checkbox>
|
||||
</view>
|
||||
</wd-checkbox-group>
|
||||
</scroll-view>
|
||||
<view class="pb-safe border-#eef0f5 border-t-solid border-1 flex flex-items-center">
|
||||
<view
|
||||
@click="confirm"
|
||||
class="opacity-60% w-90% py-2 mx-5% text-center custom-bg-blue color-white rounded-2 my-3"
|
||||
>
|
||||
确定
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const instance = getCurrentInstance().proxy
|
||||
const eventChannel = instance.getOpenerEventChannel()
|
||||
|
||||
const value = ref<number[]>([])
|
||||
const selected = ref<Array>([])
|
||||
|
||||
const permissionList = [
|
||||
{
|
||||
name: '[访客类型]权限组',
|
||||
id: 0
|
||||
}
|
||||
]
|
||||
|
||||
const list = [
|
||||
{
|
||||
name: 'DS-K(FU6004429)',
|
||||
id: 1
|
||||
},
|
||||
{
|
||||
name: 'DS-K(L40959329)',
|
||||
id: 2
|
||||
}
|
||||
]
|
||||
|
||||
const change = value => {
|
||||
selected.value = []
|
||||
selected.value.push(...permissionList.filter(item => value.value.includes(item.id)))
|
||||
selected.value.push(...list.filter(item => value.value.includes(item.id)))
|
||||
}
|
||||
|
||||
const confirm = () => {
|
||||
eventChannel.emit('change', { value: selected.value })
|
||||
uni.navigateBack()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.wd-checkbox-group {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.wd-checkbox {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
276
src/pages/select/select-organization.vue
Normal file
276
src/pages/select/select-organization.vue
Normal file
@ -0,0 +1,276 @@
|
||||
<route lang="json5">
|
||||
{
|
||||
style: {
|
||||
navigationStyle: 'custom',
|
||||
disableScroll: true
|
||||
}
|
||||
}
|
||||
</route>
|
||||
<template>
|
||||
<view>
|
||||
<view v-if="showSearch" class="h-[calc(100vh-60px)] flex flex-col">
|
||||
<view v-if="systemInfo" :style="{ marginTop: systemInfo.safeAreaInsets?.top + 'px' }">
|
||||
<view class="h-12 pt-1 pb-2">
|
||||
<wd-search
|
||||
:placeholder-left="true"
|
||||
:focus="false"
|
||||
@change="changeSearch"
|
||||
@cancel="showSearch = false"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view class="flex-1 box-border" :scroll-y="true">
|
||||
<view class="bg-white">
|
||||
<view
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
class="border-[#ebebeb] border-b-1 border-b-solid px-4 flex flex-items-center"
|
||||
>
|
||||
<view
|
||||
v-if="!(type === 'member' && item.type === 'department')"
|
||||
class="mr-3"
|
||||
@click="toClick(item)"
|
||||
>
|
||||
<wd-icon
|
||||
v-if="value.includes(item)"
|
||||
name="check-circle-filled"
|
||||
color="#255cf9"
|
||||
size="26px"
|
||||
></wd-icon>
|
||||
<wd-icon v-else name="circle1" color="#838589" size="26px"></wd-icon>
|
||||
</view>
|
||||
<SearchItem class="flex-1" :item="item" @click="toClick(item)"></SearchItem>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view v-if="!showSearch" class="h-[calc(100vh-60px-env(safe-area-inset-bottom))] flex flex-col">
|
||||
<TopNavigation :title="title"></TopNavigation>
|
||||
<view
|
||||
@click="showSearch = true"
|
||||
class="bg-white flex flex-justify-center flex-items-center rounded color-#a6a6a6 mx-4 mt-3 py-1.5"
|
||||
>
|
||||
<wd-icon name="search" size="16px" class="mr-2"></wd-icon>
|
||||
<view>搜索</view>
|
||||
</view>
|
||||
<view class="flex flex-items-center m-4 text-3.5 break-all overflow-hidden whitespace-nowrap">
|
||||
<view
|
||||
v-for="(department, index) in organizationList"
|
||||
:key="department.id"
|
||||
class="flex flex-items-center"
|
||||
@click="toOrganization(index)"
|
||||
>
|
||||
<span
|
||||
:class="[index === organizationList.length - 1 ? 'color-#bfbfbf' : 'custom-color-blue']"
|
||||
>
|
||||
{{ department.name }}
|
||||
</span>
|
||||
<span v-if="index !== organizationList.length - 1" class="mx-0.5">/</span>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view class="flex-1 box-border" :scroll-y="true">
|
||||
<view class="bg-white">
|
||||
<view
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
class="border-[#ebebeb] border-b-1 border-b-solid px-4 flex flex-items-center"
|
||||
>
|
||||
<view
|
||||
v-if="!(type === 'member' && item.type === 'department')"
|
||||
class="mr-3"
|
||||
@click="toClick(item)"
|
||||
>
|
||||
<wd-icon
|
||||
v-if="value.includes(item)"
|
||||
name="check-circle-filled"
|
||||
color="#255cf9"
|
||||
size="26px"
|
||||
></wd-icon>
|
||||
<wd-icon v-else name="circle1" color="#838589" size="26px"></wd-icon>
|
||||
</view>
|
||||
<OrganizationItem
|
||||
class="flex-1"
|
||||
:item="item"
|
||||
:disabled="item.type === 'department' && value.includes(item)"
|
||||
@click="toClick(item)"
|
||||
></OrganizationItem>
|
||||
</view>
|
||||
</view>
|
||||
<view class="py-5 px-15">
|
||||
<wd-divider color="#838589">仅显示已开通账号的人员</wd-divider>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view
|
||||
class="pb-safe h-60px px-4 border-[#ebebeb] border-t-1 border-t-solid text-3.5 flex flex-items-center"
|
||||
>
|
||||
<view class="flex flex-items-center flex-justify-between w-full">
|
||||
<view class="flex flex-items-center" v-if="value.length" @click="showSelected = true">
|
||||
<view class="custom-color-blue">已选中:{{ value.length }}个人</view>
|
||||
<wd-icon name="arrow-right" size="16px" class="ml-2" color="#255cf7"></wd-icon>
|
||||
</view>
|
||||
<view v-else></view>
|
||||
<view class="custom-bg-blue color-white py-1 px-5 rounded" @click="confirm">确定</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<wd-popup v-model="showSelected" position="bottom">
|
||||
<view class="h-100vh flex flex-col bg-#f3f5fa">
|
||||
<TopNavigation
|
||||
title="已选中"
|
||||
:have-back="false"
|
||||
right-button-text="确定"
|
||||
@right-button="showSelected = false"
|
||||
></TopNavigation>
|
||||
<scroll-view class="flex-1 box-border" :scroll-y="true">
|
||||
<view class="bg-white">
|
||||
<view
|
||||
v-for="item in value"
|
||||
:key="item.id"
|
||||
class="flex flex-items-center px-4 py-3 border-[#ebebeb] border-b-1 border-b-solid"
|
||||
>
|
||||
<image :src="item.avatar" class="w-10 h-10 rounded-2"></image>
|
||||
<view class="ml-4 text-3.5">
|
||||
<view>{{ item.name }}</view>
|
||||
<view class="mt-1 color-#a6a6a6 text-3">{{ item.name }}</view>
|
||||
</view>
|
||||
<view class="p-1 ml-a bg-#f3f5f8 rounded-50%" @click="deleteItem(item)">
|
||||
<image src="/static/images/icon_delete.png" class="w-5 h-5"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useBasicStore } from '@/store'
|
||||
import OrganizationItem from '@/pages/select/components/OrganizationItem.vue'
|
||||
import SearchItem from '@/pages/select/components/SearchItem.vue'
|
||||
import GetSystemInfoResult = UniNamespace.GetSystemInfoResult
|
||||
|
||||
const instance = getCurrentInstance().proxy
|
||||
const eventChannel = instance.getOpenerEventChannel()
|
||||
|
||||
const type = ref<string>('')
|
||||
const multiple = ref<boolean>(false)
|
||||
const title = ref<string>('')
|
||||
const showSearch = ref<boolean>(false)
|
||||
|
||||
const organizationList = ref<Array<object>>([
|
||||
{
|
||||
id: 1,
|
||||
type: 'department',
|
||||
name: '技术部'
|
||||
}
|
||||
])
|
||||
|
||||
const $basic = useBasicStore()
|
||||
|
||||
const systemInfo = ref<GetSystemInfoResult>(null)
|
||||
|
||||
const value = ref<Array>([])
|
||||
const showSelected = ref<boolean>(false)
|
||||
|
||||
const list = ref<Array<object>>([
|
||||
{
|
||||
id: 0,
|
||||
type: 'department',
|
||||
name: 'xxx的互联',
|
||||
avatar: '/static/images/icon_file.png'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
type: 'member',
|
||||
name: '张三',
|
||||
avatar: '/static/images/icon_default_avatar.png'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
type: 'member',
|
||||
name: '李四',
|
||||
avatar: '/static/images/icon_default_avatar.png'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
type: 'member',
|
||||
name: '王五',
|
||||
avatar: '/static/images/icon_default_avatar.png'
|
||||
}
|
||||
])
|
||||
|
||||
const toClick = item => {
|
||||
if (value.value.includes(item)) {
|
||||
value.value = value.value.filter(v => v !== item)
|
||||
} else {
|
||||
if (multiple.value || value.value.length === 0) {
|
||||
value.value.push(item)
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '最多选择一个',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const deleteItem = item => {
|
||||
value.value = value.value.filter(v => v !== item)
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
systemInfo.value = await $basic.getSystemInfo()
|
||||
})
|
||||
|
||||
function changeSearch({ value }) {
|
||||
console.log('输入', value)
|
||||
}
|
||||
|
||||
onLoad(options => {
|
||||
const params = JSON.parse(options.params)
|
||||
type.value = params.type
|
||||
multiple.value = params.multiple
|
||||
title.value = params.title
|
||||
})
|
||||
|
||||
const toOrganization = index => {
|
||||
if (index === organizationList.value.length - 1) {
|
||||
return
|
||||
}
|
||||
uni.navigateBack({
|
||||
delta: organizationList.value.length - index - 1
|
||||
})
|
||||
}
|
||||
|
||||
const change = e => {
|
||||
console.log('change', e)
|
||||
}
|
||||
|
||||
const confirm = () => {
|
||||
eventChannel.emit('change', { value: value.value })
|
||||
uni.navigateBack({
|
||||
delta: organizationList.value.length
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
:deep(.wd-checkbox) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.wd-search {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
:deep(.wd-search__block) {
|
||||
padding: 3px 0;
|
||||
background-color: #e2e5ea;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
:deep(.wd-search__cancel) {
|
||||
color: #a6a6a6;
|
||||
}
|
||||
</style>
|
||||
BIN
src/static/images/icon_airplane.png
Normal file
BIN
src/static/images/icon_airplane.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
BIN
src/static/images/icon_approval.png
Normal file
BIN
src/static/images/icon_approval.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/static/images/icon_default_avatar.png
Normal file
BIN
src/static/images/icon_default_avatar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
BIN
src/static/images/icon_delete.png
Normal file
BIN
src/static/images/icon_delete.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src/static/images/icon_file.png
Normal file
BIN
src/static/images/icon_file.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
21
src/types/uni-pages.d.ts
vendored
21
src/types/uni-pages.d.ts
vendored
@ -4,10 +4,10 @@
|
||||
// Generated by vite-plugin-uni-pages
|
||||
|
||||
interface NavigateToOptions {
|
||||
url: "/pages/mine/mine" |
|
||||
"/pages/home/home" |
|
||||
"/pages/application-list/application-list" |
|
||||
url: "/pages/home/home" |
|
||||
"/pages/approval/approval-detail" |
|
||||
"/pages/approval/approval" |
|
||||
"/pages/approval/create-application" |
|
||||
"/pages/attendance/allowed-time" |
|
||||
"/pages/attendance/attendance-rules" |
|
||||
"/pages/attendance/attendance" |
|
||||
@ -16,18 +16,27 @@ interface NavigateToOptions {
|
||||
"/pages/attendance/flexible-punching" |
|
||||
"/pages/attendance/issue-record-detail" |
|
||||
"/pages/attendance/issue-record" |
|
||||
"/pages/code/code" |
|
||||
"/pages/get-code/get-code" |
|
||||
"/pages/home/application-list" |
|
||||
"/pages/info-publish/add-release-plan" |
|
||||
"/pages/info-publish/announce-notice" |
|
||||
"/pages/info-publish/basic-info" |
|
||||
"/pages/info-publish/drafts-list" |
|
||||
"/pages/info-publish/edit-pic-video" |
|
||||
"/pages/info-publish/info-publish" |
|
||||
"/pages/info-publish/material-library" |
|
||||
"/pages/info-publish/notice-details" |
|
||||
"/pages/info-publish/notice-manage" |
|
||||
"/pages/info-publish/play-content-library" |
|
||||
"/pages/info-publish/release-plan" |
|
||||
"/pages/login/code" |
|
||||
"/pages/login/get-code" |
|
||||
"/pages/login/login" |
|
||||
"/pages/login/reset-password" |
|
||||
"/pages/mine/mine" |
|
||||
"/pages/notification/notification" |
|
||||
"/pages/personnel-passage/traffic-correlation" |
|
||||
"/pages/reset-password/reset-password" |
|
||||
"/pages/select/select-access-control" |
|
||||
"/pages/select/select-organization" |
|
||||
"/pages/workbench/workbench" |
|
||||
"/pages/attendance/attendance-add-group/attendance-add-group" |
|
||||
"/pages/attendance/attendance-add-group/attendance-device" |
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user