Merge branch 'develop' into develop_wsy

# Conflicts:
#	src/pages.json
#	src/types/uni-pages.d.ts
This commit is contained in:
魏少阳 2025-01-24 13:56:05 +08:00
commit 237b36a89f
67 changed files with 4375 additions and 244 deletions

View File

@ -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 () => {

View File

@ -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()
})

View File

@ -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",
@ -207,11 +267,13 @@
"needLogin": false
},
{
"path": "pages/mine/mine",
"path": "pages/login/reset-password",
"type": "page",
"style": {
"navigationStyle": "custom"
}
"navigationStyle": "custom",
"disableScroll": true
},
"needLogin": false
},
{
"path": "pages/notification/notification",
@ -226,13 +288,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",
@ -433,4 +502,4 @@
}
],
"subPackages": []
}
}

View 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>

View File

@ -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)

View 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>

View 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>

View File

@ -0,0 +1,151 @@
<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="w-10 text-center break-all overflow-hidden text-ellipsis line-clamp-1"
>
213321123321312213321123
</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 class="mb-2 w-fit mr-2">
<view
class="h-10 w-10 rounded-2 border-#255cf7 border-1 border-solid flex flex-items-center flex-justify-center box-border"
>
<wd-icon name="add" size="22px" color="#255cf7"></wd-icon>
</view>
</view>
<view v-for="item in 8" :key="item" class="mb-2 w-fit mr-2">
<view class="border-0">
<image
src="https://file.hikmall.com/prod/image/4921b7596d2344088f8611f503b011dc.png"
class="h-10 w-10 rounded-2"
mode="aspectFill"
></image>
<view
class="w-10 text-center break-all overflow-hidden text-ellipsis line-clamp-1"
>
xxx
</view>
</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="w-10 text-center break-all overflow-hidden text-ellipsis line-clamp-1"
>
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="w-10 text-center break-all overflow-hidden text-ellipsis line-clamp-1"
>
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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View File

@ -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>

View File

@ -50,7 +50,7 @@
const clickMenu = () => {
uni.navigateTo({
url: '/pages/application-list/application-list'
url: '/pages/home/application-list'
})
}

View File

@ -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')
@ -269,7 +270,8 @@
{
id: 6093,
icon: 'https://file.hikmall.com/prod/image/8ed90fc35e3840e782d73676efcc9a30.png',
name: '信息发布'
name: '信息发布',
url: '/pages/info-publish/info-publish'
}
]
},
@ -322,11 +324,4 @@
const colorChange = data => {
color.value = data
}
const clickItem = item => {
console.log(item)
uni.navigateTo({
url: item.url
})
}
</script>

View File

@ -0,0 +1,281 @@
<route lang="json5" type="page">
{
layout: 'default',
style: {
navigationStyle: 'custom'
}
}
</route>
<template>
<view class="h-[calc(100vh-0px)] flex flex-col">
<TopNavigation title="添加发布计划"></TopNavigation>
<scroll-view class="flex-1 box-border" scroll-y>
<view class="plan-form">
<!-- 计划名称 -->
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
<text>计划名称</text>
</view>
<input
type="text"
v-model="formData.name"
placeholder="智慧屏202501171038843"
class="form-input"
/>
</view>
<!-- 计划描述 -->
<view class="form-item">
<view class="form-label">计划描述</view>
<input
type="text"
v-model="formData.description"
placeholder="请输入"
class="form-input"
/>
</view>
<!-- 播放规则 -->
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
<text>播放规则</text>
</view>
<view class="form-value" @click="handleSelectRule">
<text>每天循环播放</text>
<text class="arrow">></text>
</view>
</view>
<!-- 播放时间与内容 -->
<view class="form-section">
<view class="section-header">
<text class="required">*</text>
<text>播放时间与内容</text>
</view>
<view class="time-content-card">
<view class="time-row">
<text class="label">时段</text>
<text class="value">00:00 23:59</text>
<text class="action-icon"></text>
<text class="delete-icon">🗑</text>
</view>
<view class="content-row">
<text class="label">内容</text>
<text class="value ellipsis">智慧屏节目2024...</text>
</view>
</view>
<view class="add-time-btn" @click="handleAddTime">
<text class="plus-icon">+</text>
<text>添加时段</text>
</view>
</view>
<!-- 关联设备 -->
<view class="form-item">
<view class="form-label">
<text class="required">*</text>
<text>关联设备</text>
</view>
<view class="form-value" @click="handleSelectDevice">
<text class="placeholder">请选择</text>
<text class="arrow">></text>
</view>
</view>
</view>
</scroll-view>
<!-- 底部按钮 -->
<view class="footer">
<button class="submit-btn" @click="handleSubmit">保存并发布</button>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
//
const formData = ref({
name: '',
description: '',
rule: '每天循环播放',
timeSlots: [],
devices: []
})
//
const handleSelectRule = () => {
//
}
//
const handleAddTime = () => {
//
}
//
const handleSelectDevice = () => {
//
}
//
const handleSubmit = () => {
//
}
</script>
<style lang="scss" scoped>
page {
background-color: #f5f5f5;
}
.plan-form {
margin: 16px;
}
.form-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px;
margin-bottom: 12px;
background: #fff;
border-radius: 8px;
}
.form-label {
display: flex;
align-items: center;
font-size: 15px;
color: #333;
.required {
margin-right: 4px;
color: #ff4d4f;
}
}
.form-input {
flex: 1;
font-size: 14px;
color: #333;
text-align: right;
}
.form-value {
display: flex;
align-items: center;
font-size: 14px;
color: #333;
.placeholder {
color: #999;
}
.arrow {
margin-left: 8px;
color: #ccc;
}
}
.form-section {
padding: 16px;
margin-bottom: 12px;
background: #fff;
border-radius: 8px;
.section-header {
display: flex;
align-items: center;
margin-bottom: 16px;
font-size: 15px;
color: #333;
.required {
margin-right: 4px;
color: #ff4d4f;
}
}
}
.time-content-card {
padding: 12px;
margin-bottom: 12px;
background: #f5f7fa;
border-radius: 4px;
.time-row,
.content-row {
display: flex;
align-items: center;
margin-bottom: 8px;
&:last-child {
margin-bottom: 0;
}
.label {
width: 40px;
font-size: 14px;
color: #666;
}
.value {
flex: 1;
margin: 0 8px;
font-size: 14px;
color: #333;
}
.action-icon,
.delete-icon {
padding: 4px;
color: #999;
}
}
}
.add-time-btn {
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
color: #4080ff;
.plus-icon {
margin-right: 4px;
}
}
.footer {
position: fixed;
right: 0;
bottom: 0;
left: 0;
padding: 16px;
background: #fff;
box-shadow: 0 -2px 6px rgba(0, 0, 0, 0.05);
.submit-btn {
width: 100%;
height: 40px;
font-size: 14px;
line-height: 40px;
color: #fff;
background: #4080ff;
border: none;
border-radius: 4px;
}
}
.ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>

View File

@ -8,100 +8,85 @@
</route>
<template>
<TopNavigation title="新增公告"></TopNavigation>
<div class="announce-notice">
<!-- 表单内容 -->
<div class="form-content">
<div class="form-item">
<div class="label">
<span class="required">*</span>
公告标题
</div>
<input
type="text"
v-model="formData.title"
placeholder="请输入公告标题"
class="input-field"
/>
</div>
<view class="h-[calc(100vh-0px)] flex flex-col">
<TopNavigation title="新增公告"></TopNavigation>
<scroll-view class="flex-1 box-border" scroll-y>
<view class="announce-notice">
<!-- 表单内容 -->
<div class="form-content">
<div class="form-item">
<div class="label">
<span class="required">*</span>
公告标题
</div>
<input
type="text"
v-model="formData.title"
placeholder="请输入公告标题"
class="input-field"
/>
</div>
<div class="form-item">
<div class="label">
<span class="required">*</span>
公告正文
<div class="form-item">
<div class="label">
<span class="required">*</span>
公告正文
</div>
<textarea
v-model="formData.content"
placeholder="请输入要发布的内容"
class="textarea-field"
@input="handleContentInput"
></textarea>
<div class="word-count">{{ contentLength }}/2000</div>
</div>
</div>
<textarea
v-model="formData.content"
placeholder="请输入要发布的内容"
class="textarea-field"
@input="handleContentInput"
></textarea>
<div class="word-count">{{ contentLength }}/2000</div>
</div>
</div>
<!-- 底部按钮 -->
<view class="bottom-btns">
<button class="nextstep-btn" @click="handleNextStep">下一步</button>
</view>
</div>
<!-- 底部按钮 -->
<view class="bottom-btns">
<button class="nextstep-btn" @click="handleNextStep">下一步</button>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
formData: {
title: '',
content: ''
}
}
},
computed: {
contentLength() {
return this.formData.content.length
}
},
methods: {
goBack() {
this.$router.back()
},
handleContentInput() {
if (this.formData.content.length > 2000) {
this.formData.content = this.formData.content.slice(0, 2000)
}
},
handleNextStep() {
uni.navigateTo({
url: '/pages/info-publish/basic-info'
})
}
<script setup lang="ts">
import { ref, computed } from 'vue'
//
const formData = ref({
title: '',
content: ''
})
//
const contentLength = computed(() => formData.value.content.length)
//
const handleContentInput = () => {
if (formData.value.content.length > 2000) {
formData.value.content = formData.value.content.slice(0, 2000)
}
}
//
const handleNextStep = () => {
uni.navigateTo({
url: '/pages/info-publish/basic-info'
})
}
</script>
<style scoped>
<style lang="scss" scoped>
page {
background-color: #f5f5f5;
}
.announce-notice {
display: flex;
flex-direction: column;
height: 100vh;
background: #f5f5f5;
}
.header {
position: relative;
display: flex;
align-items: center;
height: 44px;
padding: 0 15px;
background: #fff;
}
.title {
flex: 1;
font-size: 17px;
font-weight: 500;
text-align: center;
min-height: 100%;
}
.form-content {
@ -117,6 +102,8 @@
}
.label {
display: flex;
align-items: center;
margin-bottom: 10px;
font-size: 15px;
}
@ -156,19 +143,17 @@
padding: 10px 15px;
background-color: #fff;
box-shadow: 0 -2px 6px rgba(0, 0, 0, 0.05);
}
.nextstep-btn {
flex: 1;
height: 40px;
font-size: 14px;
line-height: 40px;
text-align: center;
border-radius: 4px;
}
.nextstep-btn {
color: #fff;
background-color: #007aff;
}
.nextstep-btn {
flex: 1;
height: 40px;
font-size: 14px;
line-height: 40px;
color: #fff;
text-align: center;
background-color: #007aff;
border: none;
border-radius: 4px;
}
</style>

View File

@ -8,98 +8,100 @@
</route>
<template>
<TopNavigation title="公告管理"></TopNavigation>
<div class="basic-info">
<!-- 基本信息区域 -->
<div class="content">
<div class="section-title">基本信息</div>
<view class="h-[calc(100vh-0px)] flex flex-col">
<TopNavigation title="公告管理"></TopNavigation>
<scroll-view class="flex-1 box-border" scroll-y>
<view class="basic-info">
<!-- 基本信息区域 -->
<view class="content">
<view class="section-title">基本信息</view>
<!-- 发布对象 -->
<div class="form-item" @click="selectTarget">
<div class="label">
<span class="required">*</span>
<span>发布对象</span>
</div>
<div class="value">
<span class="placeholder">请选择</span>
<i class="arrow">></i>
</div>
</div>
<!-- 发布对象 -->
<view class="form-item" @click="selectTarget">
<view class="label">
<text class="required">*</text>
<text>发布对象</text>
</view>
<view class="value">
<text class="placeholder">请选择</text>
<text class="arrow">></text>
</view>
</view>
<!-- 发布类型 -->
<div class="form-item" @click="showTimePicker = true">
<div class="label">
<span class="required">*</span>
<span>发布时间</span>
</div>
<div class="value">
<span>{{ publishType }}</span>
<i class="arrow">></i>
</div>
</div>
<!-- 发布类型 -->
<view class="form-item" @click="handleShowTimePicker">
<view class="label">
<text class="required">*</text>
<text>发布时间</text>
</view>
<view class="value">
<text>{{ publishType }}</text>
<text class="arrow">></text>
</view>
</view>
<!-- 发布日期 - 仅在定时发布时显示 -->
<div class="form-item" v-if="isScheduled" @click="showDatePicker = true">
<div class="label">
<span class="required">*</span>
<span>发布日期</span>
</div>
<div class="value">
<span>{{ publishDate }}</span>
<i class="arrow">></i>
</div>
</div>
<!-- 发布日期 - 仅在定时发布时显示 -->
<view class="form-item" v-if="isScheduled" @click="showDatePicker = true">
<view class="label">
<text class="required">*</text>
<text>发布日期</text>
</view>
<view class="value">
<text>{{ publishDate }}</text>
<text class="arrow">></text>
</view>
</view>
<!-- 发布时间 - 仅在定时发布时显示 -->
<div class="form-item" v-if="isScheduled" @click="showTimePickerDetail = true">
<div class="label">
<span class="required">*</span>
<span>发布时间</span>
</div>
<div class="value">
<span>{{ publishTimeDetail }}</span>
<i class="arrow">></i>
</div>
</div>
</div>
<!-- 发布时间 - 仅在定时发布时显示 -->
<view class="form-item" v-if="isScheduled" @click="showTimePickerDetail = true">
<view class="label">
<text class="required">*</text>
<text>发布时间</text>
</view>
<view class="value">
<text>{{ publishTimeDetail }}</text>
<text class="arrow">></text>
</view>
</view>
</view>
<!-- 底部按钮 -->
<div class="footer">
<button class="draft-btn" @click="saveDraft">保存为草稿</button>
<button class="publish-btn" @click="publish">发布公告</button>
</div>
<!-- 底部按钮 -->
<view class="footer">
<button class="draft-btn" @click="saveDraft">保存为草稿</button>
<button class="publish-btn" @click="publish">发布公告</button>
</view>
</view>
</scroll-view>
<!-- 发布类型选择弹窗 -->
<transition name="slide-up">
<div class="popup" v-if="showTimePicker">
<div class="mask" @click="hideTimePicker"></div>
<div class="popup-content">
<div class="popup-header">
<div class="popup-title">请选择发布时间</div>
<div class="popup-close" @click="showTimePicker = false">×</div>
</div>
<div class="popup-body">
<div
class="time-option"
:class="{ active: !isScheduled }"
@click="selectPublishType('now')"
>
立即发布
<i class="check-icon" v-if="!isScheduled"></i>
</div>
<div
class="time-option"
:class="{ active: isScheduled }"
@click="selectPublishType('schedule')"
>
定时发布
<i class="check-icon" v-if="isScheduled"></i>
</div>
</div>
</div>
</div>
</transition>
</div>
<view class="popup" v-if="showTimePicker">
<view class="mask" @click="handleCloseTimePicker"></view>
<view class="popup-content">
<view class="popup-header">
<view class="popup-title">请选择发布时间</view>
<view class="popup-close" @click="handleCloseTimePicker">×</view>
</view>
<view class="popup-body">
<view
class="time-option"
:class="{ active: !isScheduled }"
@click="selectPublishType('now')"
>
<text>立即发布</text>
<text class="check-icon" v-if="!isScheduled"></text>
</view>
<view
class="time-option"
:class="{ active: isScheduled }"
@click="selectPublishType('schedule')"
>
<text>定时发布</text>
<text class="check-icon" v-if="isScheduled"></text>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
@ -155,6 +157,14 @@
const publish = () => {
//
}
const handleShowTimePicker = () => {
showTimePicker.value = true
}
const handleCloseTimePicker = () => {
showTimePicker.value = false
}
</script>
<style scoped>
.basic-info {

View File

@ -0,0 +1,161 @@
<route lang="json5" type="page">
{
layout: 'default',
style: {
navigationStyle: 'custom'
}
}
</route>
<template>
<view class="h-[calc(100vh-0px)] flex flex-col">
<TopNavigation title="编辑图片/视频轮播"></TopNavigation>
<scroll-view class="flex-1 box-border" scroll-y>
<view class="content-form">
<!-- 名称 -->
<view class="form-item">
<view class="form-label">名称</view>
<view class="form-value">
<input
type="text"
v-model="formData.name"
placeholder="请输入名称"
class="form-input"
/>
<text v-if="formData.name" class="clear-icon" @click="clearName">×</text>
</view>
</view>
<!-- 屏幕方向 -->
<view class="form-item">
<view class="form-label">屏幕方向</view>
<view class="form-value">
<text>横屏</text>
</view>
</view>
<!-- 屏幕分辨率 -->
<view class="form-item">
<view class="form-label">屏幕分辨率</view>
<view class="form-value">
<text>1920*1080</text>
</view>
</view>
<!-- 内容类型 -->
<view class="form-item">
<view class="form-label">内容类型</view>
<view class="form-value">
<text>图片/视频轮播</text>
</view>
</view>
</view>
</scroll-view>
<!-- 底部按钮 -->
<view class="footer">
<button class="next-btn" @click="handleNext">下一步</button>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
//
const formData = ref({
name: '智慧屏节目202412260150523'
})
//
const clearName = () => {
formData.value.name = ''
}
//
const handleNext = () => {
//
}
</script>
<style lang="scss" scoped>
page {
background-color: #f5f5f5;
}
.content-form {
margin: 16px;
background: #fff;
border-radius: 8px;
}
.form-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px;
border-bottom: 1px solid #f5f5f5;
&:last-child {
border-bottom: none;
}
}
.form-label {
font-size: 15px;
color: #333;
}
.form-value {
display: flex;
flex: 1; //
align-items: center;
justify-content: flex-end; //
.form-input {
flex: 1; //
height: 20px; //
padding: 0; //
margin-left: 16px; //
font-size: 14px;
line-height: 20px;
color: #333;
text-align: right; //
background: transparent; //
border: none; //
}
.clear-icon {
flex-shrink: 0; //
width: 16px;
height: 16px;
margin-left: 8px;
line-height: 16px;
color: #999;
text-align: center;
background: #eee;
border-radius: 50%;
}
}
.footer {
position: fixed;
right: 0;
bottom: 0;
left: 0;
padding: 16px;
background: #fff;
box-shadow: 0 -2px 6px rgba(0, 0, 0, 0.05);
.next-btn {
width: 100%;
height: 40px;
font-size: 14px;
line-height: 40px;
color: #fff;
background: #4080ff;
border: none;
border-radius: 4px;
}
}
</style>

View File

@ -1,7 +1,206 @@
<route lang="json5" type="page">
{
layout: 'default',
style: {
navigationStyle: 'custom'
}
}
</route>
<template>
<view></view>
<view class="h-[calc(100vh-50px)] flex flex-col">
<TopNavigation title="信息发布"></TopNavigation>
<scroll-view class="flex-1 box-border" scroll-y>
<!-- 使用说明卡片 -->
<view class="instruction-card">
<view class="card-content">
<view class="title">信息发布使用说明</view>
<view class="subtitle">快速上手信息发布</view>
<view class="detail-btn">
看详情
<text class="arrow">></text>
</view>
</view>
<image class="card-icon" src="@/static/images/publish_icon.png" mode="aspectFit" />
</view>
<!-- 功能列表 -->
<view class="function-list">
<view
class="function-item"
v-for="(item, index) in functionList"
:key="index"
@click="navigateTo(item.path)"
>
<view class="item-left">
<image class="item-icon" :src="item.icon" mode="aspectFit" />
<view class="item-info">
<view class="item-title">{{ item.title }}</view>
<view class="item-desc">{{ item.description }}</view>
</view>
</view>
<text class="arrow">></text>
</view>
</view>
</scroll-view>
</view>
<CustomTabBar :list="tabList" :default-index="0" @change="handleTabChange"></CustomTabBar>
</template>
<script setup lang="ts"></script>
<script lang="ts" setup>
import { ref } from 'vue'
import { TabBarItem } from '@/typings'
<style scoped lang="scss"></style>
//
const functionList = ref([
{
title: '素材库',
description: '管理图片、音频、视频等素材',
icon: '/static/images/material_icon.png',
path: '/pages/info-publish/material-library'
},
{
title: '播放内容库',
description: '将文字、图片、视频素材组合成节目',
icon: '/static/images/content_icon.png',
path: '/pages/info-publish/play-content-library'
},
{
title: '发布计划',
description: '设置播放内容与时间规则同步到设备',
icon: '/static/images/plan_icon.png',
path: '/pages/info-publish/release-plan'
},
{
title: '公告管理',
description: '发布手机公告给团队成员',
icon: '/static/images/notice_icon.png',
path: '/pages/info-publish/notice-manage'
}
])
//
const tabList = ref<Array<TabBarItem>>([
{
title: '首页',
icon: 'home'
},
{
title: '屏幕控制',
icon: 'home'
}
])
//
const navigateTo = (path: string) => {
uni.navigateTo({
url: path
})
}
//
const handleTabChange = (data: { value: number }) => {
//
}
</script>
<style lang="scss" scoped>
page {
background-color: #f6f8fc;
}
/* 通用箭头样式 - 放在最前面 */
.arrow {
font-size: 16px;
color: #ccc;
}
/* 说明卡片样式 */
.instruction-card {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px;
margin: 16px;
color: #fff;
background: #4080ff;
border-radius: 12px;
.card-content {
flex: 1;
}
.title {
margin-bottom: 8px;
font-size: 18px;
font-weight: 500;
}
.subtitle {
font-size: 14px;
opacity: 0.8;
}
.detail-btn {
display: inline-flex;
align-items: center;
padding: 4px 12px;
margin-top: 12px;
font-size: 14px;
background: rgba(255, 255, 255, 0.2);
border-radius: 16px;
.arrow {
color: #fff; /* 覆盖通用箭头样式 */
}
}
.card-icon {
width: 60px;
height: 60px;
margin-left: 20px;
}
}
/* 功能列表样式 */
.function-list {
margin: 16px;
overflow: hidden;
background: #fff;
border-radius: 12px;
.function-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px;
border-bottom: 1px solid #f5f5f5;
&:last-child {
border-bottom: none;
}
}
.item-left {
display: flex;
align-items: center;
}
.item-icon {
width: 30px;
height: 30px;
margin-right: 12px;
}
.item-info {
.item-title {
margin-bottom: 4px;
font-size: 16px;
font-weight: 500;
color: #333;
}
.item-desc {
font-size: 12px;
color: #999;
}
}
}
</style>

View File

@ -0,0 +1,309 @@
<route lang="json5" type="page">
{
layout: 'default',
style: {
navigationStyle: 'custom'
}
}
</route>
<template>
<view class="h-[calc(100vh-0px)] flex flex-col">
<TopNavigation title="素材库"></TopNavigation>
<scroll-view class="flex-1 box-border" scroll-y>
<!-- 存储容量卡片 -->
<view class="storage-card">
<view class="storage-title">素材存储容量</view>
<view class="storage-info">
<text class="used-text">已用 0.00%</text>
<text class="remain-text">剩余19.99 G/ 20.00 G</text>
</view>
<view class="progress-bar">
<view class="progress" :style="{ width: usedPercentage + '%' }"></view>
</view>
<view class="storage-details">
<view class="detail-item">
<text class="size image">833.67 K</text>
<text class="type">图片</text>
</view>
<view class="detail-item">
<text class="size audio">0 B</text>
<text class="type">音频</text>
</view>
<view class="detail-item">
<text class="size video">0 B</text>
<text class="type">视频</text>
</view>
</view>
</view>
<!-- 素材类型标签 -->
<view class="material-tabs">
<view
v-for="(tab, index) in tabs"
:key="index"
class="tab-item"
:class="{ active: currentTab === index }"
@click="handleTabChange(index)"
>
<text>{{ tab.name }}({{ tab.count }})</text>
</view>
</view>
<!-- 素材列表 -->
<view class="material-list">
<view v-for="(item, index) in materialList" :key="index" class="material-item">
<image class="material-icon" :src="item.icon" mode="aspectFill" />
<view class="material-info">
<text class="material-name">{{ item.name }}</text>
<text class="material-id">{{ item.id }}</text>
<text class="material-time">{{ item.time }}</text>
</view>
<view class="right-wrapper">
<text class="status-tag">已通过</text>
<text class="arrow">></text>
</view>
</view>
</view>
</scroll-view>
<!-- 底部按钮 -->
<view class="footer">
<button class="upload-btn">上传列表</button>
<button class="add-btn">添加图片素材</button>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
// 使
const usedPercentage = ref(0.0)
//
const tabs = ref([
{ name: '图片', count: 4 },
{ name: '音频', count: 0 },
{ name: '视频', count: 0 }
])
//
const currentTab = ref(0)
//
const materialList = ref([
{
icon: 'https://file.hikmall.com/prod/image/885eb6ac104e4a76941e67eb738142ec.png',
name: 'house_resource.png',
id: 'HikMall_30013234',
time: '2024-12-26 15:11:18'
},
{
icon: 'https://file.hikmall.com/prod/image/885eb6ac104e4a76941e67eb738142ec.png',
name: 'app_icon.png',
id: 'HikMall_30013234',
time: '2024-12-26 15:10:37'
},
{
icon: 'https://file.hikmall.com/prod/image/885eb6ac104e4a76941e67eb738142ec.png',
name: 'home.png',
id: 'HikMall_30013234',
time: '2024-12-26 15:05:10'
},
{
icon: 'https://file.hikmall.com/prod/image/885eb6ac104e4a76941e67eb738142ec.png',
name: '锁通通 2.jpg',
id: 'HikMall_30013234',
time: '2024-12-26 15:05:10'
}
])
//
const handleTabChange = (index: number) => {
currentTab.value = index
}
</script>
<style lang="scss" scoped>
page {
background-color: #f5f5f5;
}
.storage-card {
padding: 20px;
margin: 16px;
background: #fff;
border-radius: 12px;
.storage-title {
margin-bottom: 12px;
font-size: 16px;
font-weight: 500;
}
.storage-info {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
font-size: 14px;
}
.progress-bar {
height: 4px;
margin-bottom: 16px;
background: #f0f0f0;
border-radius: 2px;
.progress {
height: 100%;
background: #4080ff;
border-radius: 2px;
}
}
.storage-details {
display: flex;
justify-content: space-between;
}
.detail-item {
display: flex;
flex-direction: column;
align-items: center;
.size {
margin-bottom: 4px;
font-size: 14px;
&.image {
color: #4080ff;
}
&.audio {
color: #00c1d4;
}
&.video {
color: #ffb100;
}
}
.type {
font-size: 12px;
color: #999;
}
}
}
.material-tabs {
display: flex;
padding: 0 16px;
margin-bottom: 12px;
.tab-item {
position: relative;
padding: 8px 0;
margin-right: 24px;
font-size: 14px;
color: #999;
&.active {
font-weight: 500;
color: #333;
&::after {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 2px;
content: '';
background: #4080ff;
}
}
}
}
.material-list {
padding: 0 16px;
.material-item {
position: relative; //
display: flex;
align-items: center;
padding: 16px;
margin-bottom: 12px;
background: #fff;
border-radius: 8px;
}
.material-icon {
width: 48px;
height: 48px;
margin-right: 12px;
border-radius: 4px;
}
.material-info {
flex: 1;
.material-name {
margin-bottom: 4px;
font-size: 14px;
color: #333;
}
.material-id,
.material-time {
display: block;
font-size: 12px;
color: #999;
}
}
.status-tag {
position: absolute; //
top: 0; // 8px
right: 0; // 16px
padding: 2px 6px; //
font-size: 12px;
color: #52c41a;
background-color: rgba(82, 196, 26, 0.1); // 绿
border-radius: 2px; //
}
.arrow {
margin-top: 24px; //
color: #ccc;
}
}
.footer {
position: fixed;
right: 0;
bottom: 0;
left: 0;
display: flex;
padding: 10px 16px;
background: #fff;
box-shadow: 0 -2px 6px rgba(0, 0, 0, 0.05);
button {
flex: 1;
height: 40px;
font-size: 14px;
border: none;
border-radius: 4px;
&.upload-btn {
margin-right: 12px;
color: #333;
background: #f5f5f5;
}
&.add-btn {
color: #fff;
background: #4080ff;
}
}
}
</style>

View File

@ -0,0 +1,235 @@
<route lang="json5" type="page">
{
layout: 'default',
style: {
navigationStyle: 'custom'
}
}
</route>
<template>
<view class="h-[calc(100vh-0px)] flex flex-col">
<TopNavigation title="播放内容库"></TopNavigation>
<scroll-view class="flex-1 box-border" scroll-y>
<!-- 设备类型切换 -->
<view class="device-tabs">
<view
v-for="(tab, index) in deviceTabs"
:key="index"
class="tab-item"
:class="{ active: currentDeviceTab === index }"
@click="handleDeviceTabChange(index)"
>
<text>{{ tab.name }}</text>
</view>
</view>
<!-- 右上角按钮 -->
<!-- <view class="action-btn">
<text>图片/视频轮播</text>
</view> -->
<!-- 内容卡片 -->
<view class="content-card">
<view class="card-header">
<view class="content-title">智慧屏节目202412260150523</view>
<view class="content-type">图片/视频轮播</view>
</view>
<view class="content-info">
<view class="info-item">
<text class="label">分辨率</text>
<text class="value">1920*1080</text>
</view>
<view class="info-item">
<text class="label">更新人员</text>
<text class="value">HikMall_30013234</text>
</view>
<view class="info-item">
<text class="label">更新时间</text>
<text class="value">2024-12-26 15:05:31</text>
</view>
</view>
<!-- 操作按钮组 -->
<view class="action-group">
<button class="action-button" @click="handlePreview">预览</button>
<button class="action-button" @click="handleEdit">编辑</button>
<button class="action-button" @click="handleDelete">删除</button>
<button class="action-button" @click="handlePublish">发布</button>
</view>
</view>
</scroll-view>
<!-- 底部按钮 -->
<view class="footer">
<button class="add-btn">添加智慧屏节目</button>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
//
const deviceTabs = ref([{ name: '智慧屏' }, { name: '门禁' }])
//
const currentDeviceTab = ref(0)
//
const handleDeviceTabChange = (index: number) => {
currentDeviceTab.value = index
}
//
const handlePreview = () => {
//
}
const handleEdit = () => {
//
uni.navigateTo({
url: '/pages/info-publish/edit-pic-video'
})
}
const handleDelete = () => {
//
}
const handlePublish = () => {
//
uni.navigateTo({
url: '/pages/info-publish/add-release-plan'
})
}
</script>
<style lang="scss" scoped>
page {
background-color: #f5f5f5;
}
.device-tabs {
display: flex;
padding: 8px 16px;
margin: 16px;
background: #fff;
border-radius: 8px;
.tab-item {
flex: 1;
padding: 8px 0;
font-size: 14px;
color: #666;
text-align: center;
&.active {
font-weight: 500;
color: #333;
background: #f5f5f5;
border-radius: 4px;
}
}
}
.action-btn {
position: absolute;
top: 16px;
right: 16px;
padding: 6px 12px;
font-size: 12px;
color: #fff;
background: #4080ff;
border-radius: 16px;
}
.content-card {
padding: 16px;
margin: 16px;
background: #fff;
border-radius: 8px;
.card-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-bottom: 16px;
}
.content-title {
flex: 1;
margin-right: 12px; //
font-size: 16px;
font-weight: 500;
color: #333;
}
.content-type {
padding: 2px 8px;
margin-top: 0px;
font-size: 12px;
color: #4080ff;
white-space: nowrap;
background: rgba(64, 128, 255, 0.1);
border-radius: 4px;
}
.info-item {
display: flex;
justify-content: space-between;
margin-bottom: 12px;
font-size: 14px;
&:last-child {
margin-bottom: 14px;
}
.label {
color: #666;
}
.value {
color: #333;
}
}
}
.action-group {
display: flex;
gap: 8px;
justify-content: space-between;
.action-button {
flex: 1;
height: 32px;
font-size: 14px;
line-height: 32px;
color: #4080ff;
background: #fff;
border: 1px solid #4080ff;
border-radius: 4px;
}
}
.footer {
position: fixed;
right: 0;
bottom: 0;
left: 0;
padding: 16px;
background: #fff;
box-shadow: 0 -2px 6px rgba(0, 0, 0, 0.05);
.add-btn {
width: 100%;
height: 40px;
font-size: 14px;
line-height: 40px;
color: #fff;
background: #4080ff;
border: none;
border-radius: 4px;
}
}
</style>

View File

@ -0,0 +1,138 @@
<route lang="json5" type="page">
{
layout: 'default',
style: {
navigationStyle: 'custom'
}
}
</route>
<template>
<view class="h-[calc(100vh-0px)] flex flex-col">
<TopNavigation title="发布计划"></TopNavigation>
<scroll-view class="flex-1 box-border" scroll-y>
<!-- 设备类型切换 -->
<view class="device-tabs">
<view
v-for="(tab, index) in deviceTabs"
:key="index"
class="tab-item"
:class="{ active: currentDeviceTab === index }"
@click="handleDeviceTabChange(index)"
>
<text>{{ tab.name }}</text>
</view>
</view>
<!-- 空状态展示 -->
<view class="empty-state" v-if="!hasData">
<image class="empty-icon" src="/static/images/empty-box.png" mode="aspectFit" />
<text class="empty-text">暂无数据</text>
</view>
</scroll-view>
<!-- 底部按钮组 -->
<view class="footer">
<button class="history-btn">下发记录</button>
<button class="add-btn">添加计划</button>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
//
const deviceTabs = ref([{ name: '智慧屏' }, { name: '门禁' }])
//
const currentDeviceTab = ref(0)
//
const hasData = ref(false)
//
const handleDeviceTabChange = (index: number) => {
currentDeviceTab.value = index
}
</script>
<style lang="scss" scoped>
page {
background-color: #f5f5f5;
}
.device-tabs {
display: flex;
padding: 4px;
margin: 16px;
background: #fff;
border-radius: 8px;
.tab-item {
flex: 1;
padding: 8px 0;
font-size: 14px;
color: #666;
text-align: center;
border-radius: 4px;
&.active {
font-weight: 500;
color: #333;
background: #f5f5f5;
}
}
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 200px;
.empty-icon {
width: 120px;
height: 120px;
margin-bottom: 16px;
}
.empty-text {
font-size: 14px;
color: #999;
}
}
.footer {
position: fixed;
right: 0;
bottom: 0;
left: 0;
display: flex;
gap: 12px; //
padding: 16px;
background: #fff;
box-shadow: 0 -2px 6px rgba(0, 0, 0, 0.05);
button {
height: 40px;
font-size: 14px;
line-height: 40px;
border: none;
border-radius: 4px;
}
.history-btn {
flex: 1;
color: #333;
background: #f5f5f5;
}
.add-btn {
flex: 1;
color: #fff;
background: #4080ff;
}
}
</style>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -226,4 +226,8 @@
}
</script>
<style scoped lang="scss"></style>
<style lang="scss">
page {
background-color: #ffffff;
}
</style>

View 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>

View 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>

View 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>

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@ -6,7 +6,9 @@
interface NavigateToOptions {
url: "/pages/home/home" |
"/pages/application-list/application-list" |
"/pages/approval/approval-detail" |
"/pages/approval/approval" |
"/pages/approval/create-application" |
"/pages/attendance/allowed-time" |
"/pages/attendance/attendance-rules" |
"/pages/attendance/attendance" |
@ -15,19 +17,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/mine/mine" |
"/pages/login/reset-password" |
"/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" |