This commit is contained in:
TimSpan 2024-12-18 10:06:41 +08:00
commit 895c0c8c62
460 changed files with 17145 additions and 3812 deletions

View File

@ -1,3 +1,10 @@
# 配置文档参考 https://taro-docs.jd.com/docs/next/env-mode-config
TARO_APP_ID="wx0acd1c4fcf94bdd3"
# TARO_APP_BASE_API="http://172.10.10.93:8765"
TARO_APP_BASE_API="http://172.10.10.93:8765"
# minio
TARO_APP_MINIO_URL=http://118.253.177.137:9000
TARO_APP_MINIO_BUCKET=police-security-dev

View File

@ -1 +1,6 @@
# TARO_APP_ID="生产环境下的小程序appid"
# TARO_APP_ID="wx0acd1c4fcf94bdd3"
TARO_APP_BASE_API="https://www.hnjinglian.cn:5678"
# minio
TARO_APP_MINIO_URL=https://www.hnjinglian.cn:9002
TARO_APP_MINIO_BUCKET=police-security

View File

@ -1 +0,0 @@
# TARO_APP_ID="测试环境下的小程序appid"

View File

@ -5,11 +5,14 @@
"appid": "touristappid",
"setting": {
"urlCheck": false,
"es6": false,
"es6": true,
"enhance": false,
"minified": true,
"minifyWXSS": true,
"minifyWXML": true,
"compileHotReLoad": false,
"postcss": false,
"minified": false
"postcss": true,
"minified": true
},
"compileType": "miniprogram"
}

View File

@ -17,14 +17,24 @@ export default defineAppConfig({
'myProject/projectDetails/projectDetails',
'securityUserForm/securityUserForm',
]
}, {
},
{
root: "subPages/police",
pages: [
'dailyInspection/dailyInspection',
'myEnterprisesUnit/myEnterprisesUnit',
'myEnterprisesUnit/projectDetails/projectDetails'
]
}
},
{
root: "subPages/select",
pages: [
'dailyLife/dailyLife',
'signature/signature'
]
},
],
window: {
backgroundTextStyle: 'light',
navigationBarBackgroundColor: '#4e87ff',

View File

@ -24,6 +24,40 @@ const App = createApp({
})
}
},
onShow(){
const updateManager = Taro.getUpdateManager()
updateManager.onCheckForUpdate(function(res) {
// 请求完新版本信息的回调
if (res.hasUpdate) {
// 新版本已经下载好,调用 applyUpdate 应用新版本并重启
updateManager.applyUpdate()
}
})
updateManager.onUpdateReady(function() {
// 新版本已经准备好,可以提示用户更新
Taro.showModal({
title: '更新提示',
content: '发现新版本,是否重启应用?',
success: function (res) {
if (res.confirm) {
// 新的版本已经准备好,调用 applyUpdate 应用新版本
updateManager.applyUpdate()
}
}
}).then(res=>{
console.log(res)
})
})
updateManager.onUpdateFailed(function() {
// 新版本下载失败,可进行一些提示用户的操作
Taro.showModal({
title: '已有新版本',
content: '请删除当前小程序,重新从搜索界面打开获取最新版本',
}).then(res => {
console.log(res)
})
})
}
// 入口组件不需要实现 render 方法,即使实现了也会被 taro 所覆盖
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 500 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -20,7 +20,7 @@ export const MINI_PROGRAM_USER_CONFIG: Record<MiniProgramUserIdentity, UserConfi
},
{
pagePath: 'pages/police/mine/index',
text: '警察我的',
text: '我的',
iconPath: "assets/mine/my.png",
selectedIconPath: "assets/mine/my-active.png"
},
@ -37,7 +37,7 @@ export const MINI_PROGRAM_USER_CONFIG: Record<MiniProgramUserIdentity, UserConfi
},
{
pagePath: 'pages/projectManager/mine/index',
text: '项目经理我的',
text: '我的',
iconPath: "assets/mine/my.png",
selectedIconPath: "assets/mine/my-active.png"
},

View File

@ -1,43 +1,68 @@
.nameTitle {
position: absolute;
top: 19%;
left: 55px;
height: 125rpx;
background-color: #fff;
width: 650rpx;
border-radius: 12rpx;
box-shadow: 0px 10px 10px -4px #e3e3e3;
display: flex;
justify-content: space-between;
align-items: center;
.itemSchool {
border-right: solid 1.5rpx #dadada;
display: flex;
flex-direction: column;
align-items: center;
width: 25%;
font-size: 24px
}
.itemSchool text:nth-child(1) {
margin-bottom: 10rpx;
color: #898a8a;
margin-left: -8px;
}
}
.nameTitle .itemSchool:nth-child(4) {
border-right: none;
}
.swiperDemoItem {
color: #3886d0;
display: flex;
padding: 20px 0 0 50px;
overflow: hidden;
height: 70rpx;
margin-top: 70px;
.swiperDemoIndex {
width: 15px;
height: 45px;
background-image: linear-gradient(to bottom, #5d9cf9, #317ad9);
//background: rgb();
border-radius: 20px;
margin-right: 15px;
}
}
.Module {
background-color: #fff;
overflow: hidden;
.subModule {
display: flex;
align-items: center;
flex-wrap: wrap;
margin-right: -3px;
margin-left: -1px;
.subModuleItem {
width: 33%;
height: 180rpx;
width: 246rpx;
height: 140rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-bottom: 10rpx;
border: 1px solid #ccc;
border-left: 0;
margin-top: -1px;
.subModuleIndex {
width: 65rpx;
height: 65rpx;
width: 45rpx;
height: 45rpx;
image {
width: 100%;
height: 100%;
@ -45,3 +70,7 @@
}
}
}
}

View File

@ -0,0 +1,47 @@
.swiperDemoItem {
color: #3886d0;
display: flex;
padding: 20px 0 0 50px;
overflow: hidden;
height: 70rpx;
.swiperDemoIndex {
width: 15px;
height: 45px;
background-image: linear-gradient(to bottom, #5d9cf9, #317ad9);
//background: rgb();
border-radius: 20px;
margin-right: 15px;
}
}
.subModule {
display: flex;
align-items: center;
flex-wrap: wrap;
margin-right: -3px;
margin-left: -1px;
.subModuleItem {
width: 33%;
height: 180rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-bottom: 10rpx;
border: 1px solid #ccc;
border-left: 0;
margin-top: -1px;
.subModuleIndex {
width: 65rpx;
height: 65rpx;
image {
width: 100%;
height: 100%;
}
}
}
}

View File

@ -3,56 +3,114 @@
<view class="swiperDemo">
<nut-swiper ref="swiperRef" pagination-visible pagination-color="#e53e31" :auto-play="3000" :init-page="0">
<nut-swiper-item v-for="(item, index) in list" :key="index" style="height: 180px">
<image src="@/assets/images/01.png" alt="" style="height: 100%; width: 100%" draggable="false"/>
<image :src="item" alt="" style="height: 100%; width: 100%" draggable="false" />
</nut-swiper-item>
</nut-swiper>
</view>
<view class="nameTitle">
<view class="itemSchool">
<text>单位数量</text>
<text style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 80px; text-align: center">{{ numberStatistics.enterprisesUnitCount }}</text>
</view>
<view class="itemSchool">
<text>服务项目</text>
<text>
{{ numberStatistics.serviceProjectCount }}
</text>
</view>
<view class="itemSchool">
<text>有保安证人员</text>
<text>{{ numberStatistics.securityUserCount }}</text>
</view>
<view class="itemSchool">
<text>无保安证人员</text>
<text>{{ numberStatistics.noCardSecurityUserCount }}</text>
</view>
</view>
<view class="swiperDemoItem">
<view class="swiperDemoIndex"></view>
<view>请选择</view>
</view>
<!--九宫格-->
<view>
<view class="Module">
<view class="subModule">
<view class="subModuleItem" v-for="item in subModuleList" :key="item.id" @click="subNavigation(item.url)">
<view class="subModuleIndex">
<image :src="item.icon"></image>
</view>
<view style=" font-size: 12px;color: #414141;margin-top: 9px">{{ item.name }}</view>
<view style="font-size: 12px; color: #414141; margin-top: 5px">{{ item.name }}</view>
</view>
</view>
</view>
<view style="background-color: #e9eef4; height: 15rpx"></view>
</view>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import Taro from "@tarojs/taro";
import { onMounted, ref } from 'vue'
import Taro from '@tarojs/taro'
import icon from '@/assets/images/project.png'
import icon01 from '@/assets/images/回单.jpg'
import icon02 from '@/assets/images/工单.jpg'
import icon03 from '@/assets/images/排名.jpg'
import icon04 from '@/assets/images/法制宣传.jpg'
import icon06 from '@/assets/images/警保风采.jpg'
import './index.scss'
const list = ref(['https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg',])
import api from '@/request'
import { DataStatisticsRes } from '@/types/pages/police'
const list = ref([process.env.TARO_APP_MINIO_URL + '/police-security/2024/11/5/dunpai.jpg'])
const swiperRef = ref() //
const subModuleList = ref([
{
id: 0,
icon: icon,
name: '企事业单位',
url: '/subPages/police/myEnterprisesUnit/myEnterprisesUnit'
name: '项目管理',
url: '/subPages/police/myEnterprisesUnit/myEnterprisesUnit',
},
{
id: 1,
icon: icon,
name: '警保风采',
url: ''
icon: icon02,
name: '监督考核',
url: '/subPages/police/dailyInspection/dailyInspection',
},
{
id: 2,
icon: icon,
name: '待定',
url: ''
}
icon: icon03,
name: '考核排名',
url: '',
},
{
id: 3,
icon: icon06,
name: '警保风采',
url: '',
},
{
id: 4,
icon: icon04,
name: '法制宣传',
url: '',
},
{
id: 5,
icon: icon01,
name: '整改回单',
url: '',
},
])
const subNavigation = async (url: string) => Taro.navigateTo({url})
const numberStatistics = ref<DataStatisticsRes>({
enterprisesUnitCount: 0,
serviceProjectCount: 0,
securityUserCount: 0,
noCardSecurityUserCount: 0,
})
const dataStatistics = async () => {
const resp = await api.get<DataStatisticsRes>('/policeIndex/dataStatistics')
numberStatistics.value = resp.data as DataStatisticsRes
}
onMounted(async () => {
await dataStatistics()
})
const subNavigation = async (url: string) => Taro.navigateTo({ url })
</script>

View File

@ -0,0 +1,108 @@
<template>
<view class="search" id="search">
<nut-input v-model="valueInput" @input="searchInput" placeholder="请进行搜索"></nut-input>
<view v-if="valueInput">
<ul>
<li v-for="item in searchResults" :key="item.id">{{item}}</li>
</ul>
</view>
<view v-else >
没有找到相关结果
</view>
</view>
</template>
<script setup lang="ts">
import {onMounted, ref,} from "vue";
const valueInput = ref('')
const list = ref([])
const dataList = ref([
{
id:0,
value:'齐家园'
},
{
id:1,
value:'刘德华'
},
{
id:2,
value:'张学友'
},{
id:3,
value:'黎明'
},
{
id:4,
value:'家具城'
},
{
id:5,
value:'左岸春天'
},
{
id:6,
value:'麦德龙商城'
},
{
id:7,
value:'世纪酒店'
},
{
id:8,
value:'四方小学'
},
{
id:9,
value:'海洋半岛'
},
{
id:10,
value:'育英小学'
},
{
id:11,
value:'明德小学'
},{
id:12,
value:'希望小学',
}
])
const searchResults = ref([])
const searchInput = (e:any)=>{
valueInput.value = e.target.value
if (!valueInput.value) {
searchResults.value = []
return;
}else{
searchResults.value = list.value.filter(item =>
item.toLowerCase().includes(valueInput.value.toLowerCase())
);
}
}
onMounted(()=>{
dataList.value.map((item)=>{
return list.value.push(item.value)
})
})
</script>
<style scoped lang="scss">
.search{
padding: 0 10px;
overflow: hidden;
border: 1px solid #4e71f2;
margin: 1px 5px;
border-radius: 10px;
width: 30%;
.ceShi{
height: 300px;
background: #ccc;
}
}
.nut-input{
padding: 20rpx 20rpx;
margin: 5px 0;
}
</style>

View File

@ -0,0 +1,71 @@
.public {
height: 100vh;
background-color: #fff;
}
.public-container {
height: 320rpx;
display: flex;
align-items: center;
background-image: url('../../../assets/images/banner.png');
background-repeat: no-repeat;
background-position: right;
overflow: hidden;
padding: 45rpx;
box-sizing: border-box;
.contacts {
height: 75rpx;
width: 75rpx;
border-radius: 50%;
border: solid 1px gray;
.image {
width: 100%;
height: 100%;
}
}
.tips-text {
display: flex;
font-size: 28rpx;
color: #fff;
line-height: 50rpx;
margin-left: 20rpx;
flex-direction: column;
justify-content: space-evenly;
}
}
.exit {
height: 100rpx;
line-height: 40px;
border-bottom: solid 0.5px #ebebf7;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
color: #7d7d7d;
.exitItem {
display: flex;
align-items: center;
margin-left: 30rpx;
}
.exitItemIndex {
height: 40rpx;
width: 40rpx;
border-radius: 50%;
display: block;
line-height: 48rpx;
image {
width: 100%;
height: 100%;
}
}
.microscope {
width: 8px;
height: 8px;
display: inline-block;
border: solid 2px #ccc;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
border-bottom: white;
border-left: white;
}
}

View File

@ -1,9 +1,80 @@
<template>
<view>
警察我的
<view class="public">
<!-- 公安-->
<view class="public-container" >
<view class="contacts">
<image src="@/assets/logo/avatar1.png" mode="scaleToFill" class="image" />
</view>
<view class="tips-text">
<view style="font-size: 15px;">名字</view>
<view style="font-size: 12px;">
<text style="margin-right: 5px">部门 </text>
<text>未选择单位</text>
</view>
</view>
</view>
<!-- 用户信息 -->
<view class="userIndex">
<view class="exit" v-for="item in datalist" :key="item.value" @click="addExit(item.value)">
<view class="exitItem">
<!-- <view class="exitItemIndex">-->
<!-- <image :src="item.url" mode="scaleToFill" class="image" />-->
<!-- </view>-->
<text style="margin-left: 30rpx;font-size: 12px;">{{ item.name }}</text>
</view>
<view style="margin-right: 40rpx">
<text class="microscope"></text>
</view>
</view>
</view>
<!-- 退出弹框-->
<nut-dialog
content="是否退出登录?"
v-model:visible="visible" @cancel="visible = false" @ok="onOk"
/>
</view>
</template>
<script setup lang="ts">
import './index.scss'
import {ref} from "vue";
import {useUserStore} from "@/store/userStore";
import {useTabBarStore} from "@/store/tabBarStore"
import Taro from "@tarojs/taro";
const {setSelected} = useTabBarStore()
const {resetUserInfo} = useUserStore()
const visible = ref<boolean>(false)
const datalist = ref([
{
value: 0,
name: '简介',
},
{
value: 1,
name: '退出登录',
},
{
value: 2,
name: '修改用户信息',
},
{
value: 3,
name: '意见收集',
},
])
const addExit = (index:number)=>{
switch (index) {
case 1:
visible.value = true
break;
}
}
const onOk = ()=>{
resetUserInfo()
setSelected(0)
Taro.navigateTo({
url: "/pages/login/login",
});
}
</script>

View File

@ -1,43 +1,68 @@
.nameTitle {
position: absolute;
top: 19%;
left: 55px;
height: 125rpx;
background-color: #fff;
width: 650rpx;
border-radius: 12rpx;
box-shadow: 0px 10px 10px -4px #e3e3e3;
display: flex;
justify-content: space-between;
align-items: center;
.itemSchool {
border-right: solid 1.5rpx #dadada;
display: flex;
flex-direction: column;
align-items: center;
width: 25%;
font-size: 24px
}
.itemSchool text:nth-child(1) {
margin-bottom: 10rpx;
color: #898a8a;
margin-left: -8px;
}
}
.nameTitle .itemSchool:nth-child(4) {
border-right: none;
}
.swiperDemoItem {
color: #3886d0;
display: flex;
padding: 20px 0 0 50px;
overflow: hidden;
height: 70rpx;
margin-top: 70px;
.swiperDemoIndex {
width: 15px;
height: 45px;
background-image: linear-gradient(to bottom, #5d9cf9, #317ad9);
//background: rgb();
border-radius: 20px;
margin-right: 15px;
}
}
.Module {
background-color: #fff;
overflow: hidden;
.subModule {
display: flex;
align-items: center;
flex-wrap: wrap;
margin-right: -3px;
margin-left: -1px;
.subModuleItem {
width: 33%;
height: 180rpx;
width: 246rpx;
height: 140rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-bottom: 10rpx;
border: 1px solid #ccc;
border-left: 0;
margin-top: -1px;
.subModuleIndex {
width: 65rpx;
height: 65rpx;
width: 45rpx;
height: 45rpx;
image {
width: 100%;
height: 100%;
@ -46,3 +71,6 @@
}
}
}

View File

@ -3,56 +3,101 @@
<view class="swiperDemo">
<nut-swiper ref="swiperRef" pagination-visible pagination-color="#e53e31" :auto-play="3000" :init-page="0">
<nut-swiper-item v-for="(item, index) in list" :key="index" style="height: 180px">
<image src="@/assets/images/01.png" alt="" style="height: 100%; width: 100%" draggable="false"/>
<image :src="item" alt="" style="height: 100%; width: 100%" draggable="false" />
<view>1123</view>
</nut-swiper-item>
</nut-swiper>
</view>
<view class="nameTitle">
<view class="itemSchool">
<text>单位数量</text>
<text style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 80px; text-align: center">51</text>
</view>
<view class="itemSchool">
<text>服务项目</text>
<text> 13123 </text>
</view>
<view class="itemSchool">
<text>有保安证人员</text>
<text> 1000</text>
</view>
<view class="itemSchool">
<text>无保安证人员</text>
<text> 140</text>
</view>
</view>
<view class="swiperDemoItem">
<view class="swiperDemoIndex"></view>
<view>请选择</view>
</view>
<!--九宫格-->
<view>
<view class="Module">
<view class="subModule">
<view class="subModuleItem" v-for="item in subModuleList" :key="item.id" @click="subNavigation(item.url)">
<view class="subModuleIndex">
<image :src="item.icon"></image>
</view>
<view style=" font-size: 12px;color: #414141;margin-top: 9px">{{ item.name }}</view>
<view style="font-size: 12px; color: #414141; margin-top: 5px">{{ item.name }}</view>
</view>
</view>
</view>
<view style="background-color: #e9eef4; height: 15rpx"></view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Taro from "@tarojs/taro";
import Taro from '@tarojs/taro'
import icon from '@/assets/images/project.png'
import icon01 from '@/assets/images/回单.jpg'
import icon02 from '@/assets/images/工单.jpg'
import icon03 from '@/assets/images/排名.jpg'
import icon04 from '@/assets/images/法制宣传.jpg'
import icon06 from '@/assets/images/警保风采.jpg'
import './index.scss'
const list = ref(['https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg',])
const list = ref([process.env.TARO_APP_MINIO_URL + '/police-security/2024/11/5/dunpai.jpg'])
const swiperRef = ref() //
const subModuleList = ref([
{
id: 0,
icon: icon,
name: '我的项目',
url: '/subPages/projectManager/myProject/myProject'
name: '项目管理',
url: '/subPages/projectManager/myProject/myProject',
},
{
id: 1,
icon: icon,
name: '警保风采',
url: ''
icon: icon02,
name: '整改工单',
url: '',
},
{
id: 2,
icon: icon,
name: '待定',
url: ''
}
icon: icon03,
name: '考核排名',
url: '',
},
{
id: 3,
icon: icon06,
name: '警保风采',
url: '',
},
{
id: 4,
icon: icon04,
name: '法制宣传',
url: '',
},
{
id: 5,
icon: icon01,
name: '整改回单',
url: '',
},
])
const subNavigation = async (url: string) => Taro.navigateTo({ url })
</script>

View File

@ -0,0 +1,71 @@
.mine {
height: 100vh;
background-color: #fff;
}
.mine-container {
height: 320rpx;
display: flex;
align-items: center;
background-image: url('../../../assets/images/banner.png');
background-repeat: no-repeat;
background-position: right;
overflow: hidden;
padding: 45rpx;
box-sizing: border-box;
.contacts {
height: 75rpx;
width: 75rpx;
border-radius: 50%;
border: solid 1px gray;
.image {
width: 100%;
height: 100%;
}
}
.tips-text {
display: flex;
font-size: 28rpx;
color: #fff;
line-height: 50rpx;
margin-left: 20rpx;
flex-direction: column;
justify-content: space-evenly;
}
}
.exit {
height: 100rpx;
line-height: 40px;
border-bottom: solid 0.5px #ebebf7;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
color: #7d7d7d;
.exitItem {
display: flex;
align-items: center;
margin-left: 30rpx;
}
.exitItemIndex {
height: 40rpx;
width: 40rpx;
border-radius: 50%;
display: block;
line-height: 48rpx;
image {
width: 100%;
height: 100%;
}
}
.microscope {
width: 8px;
height: 8px;
display: inline-block;
border: solid 2px #ccc;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
border-bottom: white;
border-left: white;
}
}

View File

@ -1,9 +1,80 @@
<template>
<view>
项目经理我的
<view class="mine">
<view class="mine-container" >
<view class="contacts">
<image src="@/assets/logo/avatar1.png" mode="scaleToFill" class="image" />
</view>
<view class="tips-text">
<view style="font-size: 15px;">名字</view>
<view style="font-size: 12px;">
<text style="margin-right: 5px">保安部门 </text>
<text>未选择单位</text>
</view>
</view>
</view>
<!-- 用户信息 -->
<view class="userIndex">
<view class="exit" v-for="item in datalist" :key="item.value" @click="addExit(item.value)">
<view class="exitItem">
<!-- <view class="exitItemIndex">-->
<!-- <image :src="item.url" mode="scaleToFill" class="image" />-->
<!-- </view>-->
<text style="margin-left: 30rpx;font-size: 12px;">{{ item.name }}</text>
</view>
<view style="margin-right: 40rpx">
<text class="microscope"></text>
</view>
</view>
</view>
<!-- 退出弹框-->
<nut-dialog
content="是否退出登录?"
v-model:visible="visible" @cancel="visible = false" @ok="onOk"
/>
</view>
</template>
<script setup lang="ts">
import './index.scss'
import {ref} from "vue";
import {useUserStore} from "@/store/userStore";
import {useTabBarStore} from "@/store/tabBarStore"
import Taro from "@tarojs/taro";
const {setSelected} = useTabBarStore()
const {resetUserInfo} = useUserStore()
const visible = ref<boolean>(false)
const datalist = ref([
{
value: 0,
name: '小程序简介',
},
{
value: 1,
name: '退出登录',
},
{
value: 2,
name: '修改用户信息',
},
{
value: 3,
name: '意见收集',
},
])
const addExit = (index:number)=>{
switch (index) {
case 1:
visible.value = true
break;
}
}
const onOk = ()=>{
resetUserInfo()
setSelected(0)
Taro.navigateTo({
url: "/pages/login/login",
});
}
</script>

View File

@ -33,13 +33,7 @@
</nut-radio>
</nut-radio-group>
</nut-form-item>
<nut-form-item label="手机号:">
<nut-input
v-model="formData.telephone"
placeholder="请输入手机号码"
type="text"
/>
</nut-form-item>
<nut-form-item label="行政区划:" prop="name">
<view @click="visible = true" style="color: #808080">
{{ streetCommunitySmallCommunityLabel || "请选择行政区划" }}
@ -55,8 +49,9 @@
v-model:visible="visible"
v-model="TreeValue"
title="选择地址"
:options="TreeData"
@change="change"
lazy
:lazy-load="lazyLoad"
text-key="label"
></nut-cascader>
<nut-popup v-model:visible="show" position="bottom">
@ -95,18 +90,28 @@ const formData = ref<RegisterParams>({
});
const show = ref(false)
const visible = ref(false)
const TreeValue = ref<Record<string, any>[]>([])
const TreeData = ref(['']);
// const TreeData = ref([]);
const streetCommunitySmallCommunityLabel = ref<string>("");
const getAdministrativeDivisionTree = async () => {
const resp = await api.get<TreeNodeVo<string>[]>('/common/administrativeDivisionTree')
TreeData.value = resp.data as any
console.log(resp.data)
const getAdministrativeDivisionTree = async (value:string) => {
const resp = await api.get<TreeNodeVo<string>[]>('/common/administrativeDivisionByParentCode',{parentCode:value})
return resp.data as any
}
const change = (value: string, pathNodes: Record<string, any>[]) => {
const change = async (value: string, pathNodes: Record<string, any>[]) => {
streetCommunitySmallCommunityLabel.value = pathNodes.map((e) => e.text).join(",");
TreeValue.value = value as any
}
const lazyLoad = async (node:any, resolve:any)=>{
if (node.root) {
await resolve(getAdministrativeDivisionTree ('0'))
} else {
await resolve(getAdministrativeDivisionTree (node.value))
}
}
const columns = ref([])
const unitsList = async () => {
if (streetCommunitySmallCommunityLabel.value !== '') {
@ -126,7 +131,7 @@ const unitsList = async () => {
const selectedLabel = ref('')
const confirm = ({selectedOptions, selectedValue}) => {
const confirm = ({selectedOptions}) => {
Object.keys(selectedOptions).forEach((e) => {
selectedLabel.value = selectedOptions[e].label
formData.value.unitId = selectedOptions[e].value
@ -136,7 +141,7 @@ const confirm = ({selectedOptions, selectedValue}) => {
}
//
watch(() => formData.value.identity, (value) => {
watch(() => formData.value.identity, () => {
formData.value.unitId = ''
selectedLabel.value = ''
})
@ -155,7 +160,7 @@ const register = async () => {
identity: formData.value.identity,
unitId: formData.value.unitId
}
const resp = await api.post<string>('/miniProgramUser/register', miniProgramUserRegisterParams, {loading: true})
const resp = await api.post<string>('/mp/user/register', miniProgramUserRegisterParams, {loading: true})
Taro.showToast({
title: "注册成功",
icon: 'success',
@ -165,7 +170,7 @@ const register = async () => {
await Taro.setStorage({
key: "token",
data: resp.data,
success(res) {
success() {
Taro.navigateTo({
url: '/pages/login/login'
})
@ -209,10 +214,10 @@ const onChooseAvatar = (e) => {
formData.value.avatar = avatarUrl
}
//
const getNickname = (e) => {
formData.value.name = e.detail.value
console.log(formData.value.name)
}
// const getNickname = (e) => {
// formData.value.name = e.detail.value
// console.log(formData.value.name)
// }
onMounted(async () => {
await getAdministrativeDivisionTree()

View File

@ -0,0 +1,3 @@
export default {
"component": true
}

View File

@ -0,0 +1,85 @@
.uiwu-flex-align {
align-items: center;
}
.uiwu-flex {
display: flex;
}
.uiwu-flex-space {
align-items: center;
justify-content: space-between;
}
.uiwu-picker-search {
position: absolute;
height: 1031rpx;
left: 0;
right: 0;
bottom: 0;
background: url('../assets/images/popuptiobg.png') no-repeat 0 0 #fff;
background-size: 100% auto;
border-radius: 30rpx 30rpx 0 0;
&-btn {
padding: 20rpx 30rpx 40rpx;
text {
&:nth-child(1) {
color: #999;
}
&:nth-child(2) {
color: #00bbff;
}
}
}
&-input {
box-sizing: border-box;
height: 80rpx;
background: rgba(#f3f6fd, 0.6);
border-radius: 16rpx;
margin: 0 30rpx;
padding-left: 24rpx;
padding-right: 10rpx;
input {
font-size: 28rpx;
width: 100%;
}
}
.scroll-view {
width: 100%;
height: calc(100% - 212rpx);
margin-top: 30rpx;
}
.radio-group {
padding: 0 50rpx 30rpx;
&-item {
margin-bottom: 20rpx;
text {
font-size: 28rpx;
color: #666;
}
radio {
transform: scale(0.8);
}
}
}
}
.searchButton {
height: 60rpx;
width: 200rpx;
border-radius: 10rpx;
line-height: 60rpx;
text-align: center;
color: #fff;
background-color: #00bbff;
}

View File

@ -0,0 +1,90 @@
<template>
<view>
<view @tap.stop="open">11111</view>
<nut-popup v-model:visible="show" position="bottom" round="true">
<view style="height: 1031rpx">
<view class="uiwu-picker-search">
<view class="uiwu-picker-search-btn uiwu-flex uiwu-flex-space">
<text @tap.stop="show = false">取消</text>
<text @tap.stop="determine">确定</text>
</view>
<view class="uiwu-picker-search-input uiwu-flex uiwu-flex-align">
<input v-model="inputText.value" type="text" :placeholder="placeholder" confirm-type="search" @confirm="confirm_" />
<view @click.stop="confirm" class="searchButton">搜索</view>
</view>
<scroll-view class="scroll-view" scroll-y>
<!-- <radio-group class="radio-group" @change="change">
<view class="radio-group-item uiwu-flex uiwu-flex-space" v-for="(item, index) in listData.value" :key="index">
<text>{{ item.bank_name }}</text>
<radio :value="item.bank_name" color="#687CFF" />
</view>
</radio-group> -->
</scroll-view>
</view>
</view>
</nut-popup>
</view>
</template>
<script setup>
import './picker-search.scss'
import { ref, expose } from 'vue'
// import { searchBankName } from '@/api/app'
const props = defineProps({
placeholder: {
type: String,
default: '请输入银行名称',
},
})
//
const show = ref(true)
const listData = ref([])
const currentBankName = ref('')
const inputText = ref('')
//
const confirm = () => {
// searchBankName({ bank_name: inputText.value.trim() }).then((res) => {
// console.log('searchBankName', res)
// listData.value = res.data
// })
}
//
const confirm_ = ({ detail: { value } }) => {
console.log('confirm', value)
// searchBankName({ bank_name: value.trim() }).then((res) => {
// console.log('searchBankName', res)
// listData.value = res.data
// })
}
//
const change = ({ detail: { value } }) => {
console.log('change', value)
currentBankName.value = value
}
//
const determine = () => {
if (currentBankName.value.trim() === '') {
uni.showToast({
title: '请选择银行',
duration: 1500,
icon: 'error',
})
return
}
//
emit('change', currentBankName.value.trim())
show.value = false
}
//
const open = () => {
show.value = true
}
// expose({ open })
</script>

View File

@ -35,6 +35,9 @@ class CustomRequest {
Taro.showLoading({
title: '请求中...',
}).then()
// Taro.showLoading({
// title: '请求中...',
// })
}
Taro.request<JsonResult<T>, object>({
url: this.BASE_API + url,

View File

@ -0,0 +1,37 @@
import { defineStore } from 'pinia'
export const useDailyStore = defineStore('daily', {
state: () => ({
userdailyinspection: [],
base64_1: '',
base64_2: '',
}),
actions: {
dailyinspectionList(data) {
this.userdailyinspection = [...data]
},
change_base64_1(data) {
this.base64_1 = data
},
change_base64_2(data) {
this.base64_2 = data
},
clearSignData() {
this.base64_1 = ''
this.base64_2 = ''
},
},
getters: {
getdailyinspection(state) {
return state.userdailyinspection
},
get_base64_1(state) {
return state.base64_1
},
get_base64_2(state) {
return state.base64_2
},
}
})

View File

@ -0,0 +1,149 @@
page {
position: relative;
}
.container {
width: 100%;
}
.picker {
padding: 30rpx 30rpx;
display: flex;
justify-content: space-between;
}
.exit {
height: 100rpx;
line-height: 40px;
border-bottom: solid 0.5px #ebebf7;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
.exitItem {
display: flex;
align-items: center;
}
.iconoscope {
z-index: 9999;
width: 8px;
height: 8px;
display: inline-block;
border: solid 2px #c2c2c2;
margin-left: 10px;
transform: rotate(45deg);
border-bottom: white;
border-left: white;
}
}
.sigh_btns {
transform: rotate(-90deg);
transform-origin: center
}
.sigh_btns_noRotate {}
.input_width {
width: 400rpx;
height: 40rpx;
display: flex;
justify-content: space-between;
align-items: center
}
.uiwu-flex-align {
align-items: center;
}
.uiwu-flex {
display: flex;
}
.uiwu-flex-space {
align-items: center;
justify-content: space-between;
}
.uiwu-picker-search {
position: absolute;
height: 1031rpx;
left: 0;
right: 0;
bottom: 0;
background: url('../../../assets/images/popuptiobg.png') no-repeat 0 0 #fff;
background-size: 100% auto;
border-radius: 30rpx 30rpx 0 0;
&-btn {
padding: 20rpx 30rpx 40rpx;
text {
&:nth-child(1) {
color: #999;
}
&:nth-child(2) {
color: rgb(73, 143, 242);
}
}
}
&-input {
box-sizing: border-box;
height: 80rpx;
background: rgba(#f3f6fd, 0.6);
border-radius: 16rpx;
margin: 0 30rpx;
padding-left: 24rpx;
padding-right: 10rpx;
input {
font-size: 28rpx;
width: 100%;
}
}
.scroll-view {
width: 100%;
height: 800rpx;
margin-top: 30rpx;
}
.radio-group {
padding: 0 50rpx 30rpx;
overflow-y: scroll;
&-item {
margin-bottom: 20rpx;
text {
font-size: 28rpx;
color: #666;
}
radio {
transform: scale(0.8);
}
}
}
}
.searchButton {
height: 60rpx;
width: 200rpx;
border-radius: 10rpx;
line-height: 60rpx;
text-align: center;
color: #fff;
background-color: rgb(73, 143, 242);
}

View File

@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '监督考核',
})

View File

@ -0,0 +1,404 @@
<template>
<view class="container">
<nut-form labelWidth="320rpx" labelAlign="left" @click.stop="openSearch">
<nut-form-item label="考核单位:">
<view class="input_width">
<input style="width: 300rpx" disabled="true" v-model="enterprisesName" placeholder="请选择考核单位:" />
<IconFont name="arrow-down"></IconFont>
</view>
</nut-form-item>
</nut-form>
<!-- <picker mode="selector" :range="selector" rangeKey="label" @change="onChange">
<view class="picker">
考核单位
<view style="display: flex; align-items: center">
<view v-if="selectorChecked.length === 0" style="color: #606266">请选择考核单位</view>
<view> {{ selectorChecked }}</view>
<IconFont name="arrow-right"></IconFont>
</view>
</view>
</picker> -->
<picker v-if="(selectorType?.length ?? 0) > 0" mode="selector" :range="selectorType" rangeKey="label" @change="onChangeType">
<view class="picker">
当前考核项目
<view style="display: flex; align-items: center">
<view v-if="selectorCheckedType.length === 0" style="color: #606266">请选择考核项目</view>
<view> {{ selectorCheckedType }}</view>
<IconFont name="arrow-right"></IconFont>
</view>
</view>
</picker>
<view class="exit" v-for="item in starRating" :key="item.snowFlakeId" @click="Onrating(item.name, item.snowFlakeId)">
<view class="exitItem">
<text style="margin-left: 30rpx; font-size: 16px">
<text>{{ item.name }}{{ item?.totalScore }} </text>
</text>
</view>
<view style="margin-right: 30rpx; display: flex; align-items: center">
<view v-if="item.currentScore > 0" style="color: #ff0000"> -{{ item?.currentScore }}</view>
<IconFont name="arrow-right"></IconFont>
</view>
</view>
<view>
<nut-form labelWidth="320rpx" labelAlign="left">
<nut-form-item label="备注">
<nut-input v-model="_form.remark" placeholder="请输入备注" type="text" />
</nut-form-item>
<nut-form-item label="考核人员签名:">
<navigator :url="`/subPages/select/signature/signature?index=${1}&name=考核人员签名`" hover-class="navigator-hover">
<nut-button style="height: 50rpx" shape="square" type="info">考核人员签字</nut-button>
</navigator>
</nut-form-item>
<view style="display: flex; justify-content: center">
<image v-if="_form.assessmentUserSignature" :src="_form.assessmentUserSignature" mode="aspectFit" style="height: 300rpx"></image>
</view>
<nut-form-item label="被考评学校签名:">
<navigator :url="`/subPages/select/signature/signature?index=${2}&name=被考评学校签名`" hover-class="navigator-hover">
<nut-button style="height: 50rpx" shape="square" type="info">被考评学校负责人</nut-button>
</navigator>
</nut-form-item>
<view style="display: flex; justify-content: center">
<image v-if="_form.byAssessmentEnterprisesUnitUserSignature" :src="_form.byAssessmentEnterprisesUnitUserSignature" mode="heightFix" style="height: 300rpx"></image>
</view>
</nut-form>
</view>
<view style="height: 150rpx"></view>
<view style="display: flex; width: 100%; justify-content: center; position: fixed; bottom: 50rpx">
<nut-button :loading="isLoading" shape="round" type="info" @click="onSubmit" style="height: 80rpx; width: 702rpx; margin: 10rpx auto">确认提交</nut-button>
</view>
<nut-popup v-model:visible="show" position="bottom" :round="true">
<view style="height: 1031rpx">
<view class="uiwu-picker-search">
<view class="uiwu-picker-search-btn uiwu-flex uiwu-flex-space">
<text @tap.stop="show = false">取消</text>
<text @tap.stop="determine">确定</text>
</view>
<view class="uiwu-picker-search-input uiwu-flex uiwu-flex-align">
<input v-model="inputText" type="text" placeholder="请输入单位名称" confirm-type="search" @confirm="confirm_" />
<view @click.stop="confirm" class="searchButton">搜索</view>
</view>
<scroll-view class="scroll-view" :scroll-y="true">
<!-- <radio-group class="radio-group" @change="radioChange">
<view class="radio-group-item uiwu-flex uiwu-flex-space" v-for="(item, index) in selector" :key="index">
<text>{{ item.label }}</text>
<radio :value="item.value" color="#687CFF" />
</view>
</radio-group> -->
<nut-radio-group v-if="(selectorCopy?.length ?? 0) > 0" style="margin-left: 30rpx" v-model="selectedID">
<nut-radio icon-size="20" v-for="(item, index) in selectorCopy" :key="index" :label="item.value"> {{ item.label }}</nut-radio>
<view style="height: 40rpx"></view>
</nut-radio-group>
<nut-empty v-else description="暂无单位"></nut-empty>
</scroll-view>
</view>
</view>
</nut-popup>
</view>
<!-- <picker-search ref="picker_search" @change="changeSearch" /> -->
</template>
<script setup lang="ts">
// import PickerSearch from '@/components/PickerSearch.vue'
// import picker-search from
import { IconFont } from '@nutui/icons-vue-taro'
import Taro, { useLoad, useUnload } from '@tarojs/taro'
import './dailyInspection.scss'
import { ref, computed, reactive, watch } from 'vue'
import api from '@/request/index'
import { useDailyStore } from '@/store/daily'
// const picker_search = ref()
const starRating = ref<any[]>([])
const store = useDailyStore()
const daily = computed(() => store.getdailyinspection)
const base64_1 = computed(() => store.get_base64_1)
const base64_2 = computed(() => store.get_base64_2)
const currentCkProjectId = ref('')
const submitData = ref<Item[]>([])
const enterprisesName = ref('')
const _form = reactive({
enterprisesUnitId: '', //id
ckProjectId: '', //
assessmentUserSignature: '', //
byAssessmentEnterprisesUnitUserSignature: '', //
remark: '', //
})
watch(
[daily, base64_1, base64_2],
([newDaily, newBase64_1, newBase64_2]) => {
_form.assessmentUserSignature = newBase64_1
_form.byAssessmentEnterprisesUnitUserSignature = newBase64_2
if (newDaily.length > 0) {
starRating.value = newDaily
}
submitData.value = newDaily
// console.log('watch_______________', _form, submitData.value)
},
{ immediate: true }
)
useLoad(async () => {
await getUnitEnterprisesUnitList()
})
const Onrating = function (name: string, snowFlakeId: string) {
let index = starRating.value.findIndex((item) => item.snowFlakeId === snowFlakeId)
Taro.navigateTo({
url: `/subPages/select/dailyLife/dailyLife?name=${name}&index=${index}`,
})
}
/**
* @assessmentCriteriaRulesByCkProjectId 获取考核规则
*/
const assessmentCriteriaRulesByCkProjectId = async function (ckProjectId) {
// Taro.showLoading({
// title: '...',
// mask: true,
// })
const res = await api.get<StarRating[]>(`/mp/sa/assessmentCriteriaRulesByCkProjectId`, { ckProjectId })
res.data?.forEach((item) => {
item.currentScore = 0
item.itemList.forEach((element: ItemList) => {
element.standardList.forEach((ele) => {
ele.isSelected = false
})
element.standardList.unshift({
ckItemId: 'null',
deductionPoints: 0,
name: '达标',
snowFlakeId: 'null',
isSelected: true,
})
element.selectedID = element.standardList[0].snowFlakeId
element.selected_points = 0 // 0
//
if (element.type.value === 'multiple') {
element.selectedGroup = [element.selectedID]
}
})
})
starRating.value = res.data ?? []
// console.log('starRating.value______________________________', starRating.value)
store.dailyinspectionList(starRating.value)
// Taro.hideLoading()
}
/**
* @ckProjectListByType 根据类型获取考核标准列表
*/
const selectorCheckedType = ref<string>('')
const selectorType = ref<CkProjectListByType[]>()
const ckProjectListByType = async function (type) {
const res = await api.get<CkProjectListByType[]>(`/mp/sa/ckProjectListByType`, { type })
console.log(res.data)
if (res.data?.length === 0) {
let timeID = setTimeout(() => {
Taro.showToast({
title: '该单位下面没有考核标准',
icon: 'none',
duration: 2000,
})
clearTimeout(timeID)
}, 500)
}
selectorType.value = res.data
}
const onChangeType = function (e: any) {
let index = Number(e.detail.value)
selectorCheckedType.value = selectorType.value?.[index].label as string
currentCkProjectId.value = selectorType.value?.[index].value as string
assessmentCriteriaRulesByCkProjectId(currentCkProjectId.value)
_form.ckProjectId = selectorType.value?.[index].value as string
}
const selector = ref<UnitEnterprisesUnitList[]>()
const selectorCopy = ref<UnitEnterprisesUnitList[]>()
// const selectorChecked = ref<string>('')
// const onChange = function (e: any) {
// try {
// let index = Number(e.detail.value)
// selectorChecked.value = selector.value?.[index].label as string
// let type = selector.value?.[index].extData.type.value
// ckProjectListByType(type)
// _form.enterprisesUnitId = selector.value?.[index].value as string
// } catch (error) {
// console.log('🚀 ~ onChange ~ error:', error)
// }
// }
const getUnitEnterprisesUnitList = async function () {
const res = await api.get<UnitEnterprisesUnitList[]>(`/policeIndex/getUnitEnterprisesUnitList`)
selector.value = res.data
selectorCopy.value = res.data
}
const _showToast = function (title) {
Taro.showToast({
title,
icon: 'none',
duration: 1500,
mask: true,
})
}
/**
* @onSubmit 提交
* @assessmentRecordDetails 选择后的数据用于提交
* @clearData 清空数据
*/
const assessmentRecordDetails = ref<any[]>([])
const clearData = function () {
store.clearSignData()
store.dailyinspectionList([])
assessmentRecordDetails.value = []
_form.assessmentUserSignature = ''
_form.byAssessmentEnterprisesUnitUserSignature = ''
_form.remark = ''
assessmentCriteriaRulesByCkProjectId(currentCkProjectId.value) //
}
const isLoading = ref(false)
const onSubmit = async function () {
if (_form.enterprisesUnitId === '') {
_showToast('请选择企事业单位')
return
}
if (_form.ckProjectId === '') {
_showToast('请选择考核项目')
return
}
if (_form.assessmentUserSignature === '') {
_showToast('请考核人员签字')
return
}
if (_form.byAssessmentEnterprisesUnitUserSignature === '') {
_showToast('请被考核单位人员签字')
return
}
isLoading.value = true
submitData.value.forEach((element: StarRating) => {
element?.itemList.forEach((item: ItemList) => {
item.standardList.forEach((ele: StandardList) => {
if (ele.snowFlakeId === item.selectedID && !item.hasOwnProperty('selectedGroup') && item.selectedID != 'null') {
assessmentRecordDetails.value.push({
ckGroupId: element.snowFlakeId, //Id
ckItemId: ele.ckItemId, //ID
ckStandardId: item.selectedID, //ID
})
}
})
if (item.hasOwnProperty('selectedGroup')) {
item.selectedGroup.forEach((selectedItem) => {
item.standardList.forEach((standard_Element) => {
if (selectedItem != 'null' && selectedItem === standard_Element.snowFlakeId) {
assessmentRecordDetails.value.push({
ckGroupId: element.snowFlakeId, //Id
ckItemId: standard_Element.ckItemId, //ID
ckStandardId: selectedItem, //ID
})
}
})
})
}
})
})
const assessmentRecordParams = {
assessmentRecordDetails: [] as any[],
}
Object.assign(assessmentRecordParams, _form)
assessmentRecordParams.assessmentRecordDetails = [...assessmentRecordDetails.value]
const result = await api.post('/mp/sa/submitAssessmentRecord', assessmentRecordParams)
clearData() //
if (result.code === 200) {
isLoading.value = false
let timeID = setTimeout(() => {
Taro.showToast({
title: result.message,
icon: 'success',
duration: 2000,
mask: true,
})
clearTimeout(timeID)
}, 500)
} else {
_showToast(result.message)
}
}
useUnload(() => {
store.clearSignData()
store.dailyinspectionList([])
assessmentRecordDetails.value = []
_form.assessmentUserSignature = ''
_form.byAssessmentEnterprisesUnitUserSignature = ''
_form.remark = ''
})
const show = ref(false)
const inputText = ref('')
const selectedID = ref('')
const openSearch = () => {
show.value = true
}
const fuzzySearch = function (query) {
selectorCopy.value = selector.value?.filter((item) => item.label.includes(query))
}
const confirm = () => {
console.log(inputText.value)
if (inputText.value.trim() === '') {
selectorCopy.value = selector.value
} else {
fuzzySearch(inputText.value.trim())
}
}
const confirm_ = ({ detail: { value } }) => {
console.log('confirm', value)
if (inputText.value.trim() === '') {
selectorCopy.value = selector.value
} else {
fuzzySearch(inputText.value.trim())
}
}
const determine = () => {
console.log(selectedID.value)
if (selectedID.value === '' && (selector.value?.length as number) > 0) {
Taro.showToast({
title: '请选择单位',
icon: 'none',
duration: 1500,
mask: true,
})
return
}
let index: number = selector.value?.findIndex((item) => item.value === selectedID.value) as number
_form.enterprisesUnitId = selectedID.value
let type = selector.value?.[index].extData.type.value
enterprisesName.value = selector.value?.[index].label as string
show.value = false
ckProjectListByType(type)
}
</script>

View File

@ -17,16 +17,13 @@ page {
}
.project {
display: flex;
justify-content: space-between;
flex-flow: wrap;
text-align: center;
//display: flex;
//justify-content: space-between;
//flex-flow: wrap;
//text-align: center;
view {
width: 45%;
height: 100rpx;
border: 1px solid #cccccc;
line-height: 100rpx;
width: 100%;
margin: 8px 0 8px 0;
}
}

View File

@ -1,11 +1,12 @@
<template>
<view class="myProject">
<view class="myProjectItem" v-for="(item,index) in myProjectList" :key="index">
<view v-if="number !== 0" class="myProjectItem" v-for="(item, index) in myProjectList" :key="index">
<view style="display: flex; justify-content: space-between">
<text>{{ item?.name }}</text>
<!--<text>进行中</text>-->
<text>单位类型{{ item?.type?.label }}</text>
</view>
<view class="myProjectIndex">地址
<view class="myProjectIndex"
>地址
<text>{{ item?.provinceName }}{{ item.cityName }}{{ item.districtsName }}{{ item.streetName }}</text>
</view>
<view style="display: flex; justify-content: space-between">
@ -13,26 +14,41 @@
<text>电话{{ item?.contactPersonInfo.telephone }}</text>
</view>
<view class="project">
<view @click="projectClick(item?.name,serviceProject)" v-for="(serviceProject,index) in item.serviceProjectList"
:key="index">
{{ serviceProject.name }}
<view @click="projectClick(item?.name, serviceProject)" v-for="(serviceProject, index) in item.serviceProjectList" :key="index">
<view style="border: 1px solid #cccccc; color: #9b9b9f">
<view style="display: flex; justify-content: space-between">
<text>项目名称{{ serviceProject.name }}</text>
<text>项目类型{{ serviceProject.type?.label }}</text>
</view>
<view style="display: flex; justify-content: space-between">
<text>项目负责人{{ serviceProject.projectManagerMiniProgramUserInfo?.name ? serviceProject.projectManagerMiniProgramUserInfo?.name : '无分配项目经理' }}</text>
</view>
<view>责任单位{{ serviceProject.securityUnitName }}</view>
</view>
</view>
</view>
</view>
<view v-else class="myProject">
<nut-empty image="empty" description="暂无项目">
<div style="margin-top: 10px"></div>
</nut-empty>
</view>
</view>
</template>
<script setup lang="ts">
import api from "@/request/index";
import {onMounted, ref} from "vue";
import Taro from "@tarojs/taro";
import api from '@/request/index'
import { onMounted, ref } from 'vue'
import Taro from '@tarojs/taro'
import './myEnterprisesUnit.scss'
import {MyProjectList, ServiceProjectList} from "@/types/subPages/projectManager/myProject";
import { MyProjectList, ServiceProjectList } from '@/types/subPages/projectManager/myProject'
const myProjectList = ref<MyProjectList[]>()
const number = ref(0)
const getMyServiceProject = async () => {
const resp = await api.get<MyProjectList[]>(`/policeIndex/getUnitServiceProjectList`)
myProjectList.value = resp.data
number.value = resp.data?.length || 0
}
const projectClick = (enterprisesUnitName: string, serviceProject: ServiceProjectList) => {

View File

@ -7,9 +7,7 @@
<!-- <text>进行中</text>-->
</view>
<view>
<view style="float: left;width: 50%;" class="content">
经理名称:{{ serviceProject?.projectManagerMiniProgramUserInfo.name }}
</view>
<view style="float: left; width: 50%" class="content"> 经理名称:{{ serviceProject?.projectManagerMiniProgramUserInfo.name }} </view>
<view class="content">手机号:{{ serviceProject?.projectManagerMiniProgramUserInfo.telephone }}</view>
</view>
</view>
@ -47,9 +45,16 @@
<!--表格-->
<view class="projectDetailsTableDrop">
<view style="padding: 0 12px">项目人员</view>
<scroll-view :scroll-y="true" style="height: 80%;" @scrolltoupper="upper" @scrolltolower="lower"
:scroll-into-view="toView" :scroll-top="scrollTop" :refresherEnabled="true"
@refresherrefresh="onRefresherRefresh" :refresher-triggered="isRefresher"
<scroll-view
:scroll-y="true"
style="height: 80%"
@scrolltoupper="upper"
@scrolltolower="lower"
:scroll-into-view="toView"
:scroll-top="scrollTop"
:refresherEnabled="true"
@refresherrefresh="onRefresherRefresh"
:refresher-triggered="isRefresher"
>
<view class="projectDetailsTable" v-for="(item, index) in securityUserList" :key="index">
<view>
@ -61,12 +66,12 @@
<text>职位:{{ item.workPost ? item.workPost : '创建者' }}</text>
</view>
<view style="display: flex; justify-content: space-between">
<text>保安证件:{{ item.securityNumber ? item.securityNumber : '125241256451' }}</text>
<text>保安证件:{{ item.securityNumber ? item.securityNumber : '' }}</text>
<text>出生年月:{{ dayjs(item.dateOfBirth).format('YYYY-MM-DD') }}</text>
</view>
<view style="display: flex; justify-content: space-between">
<text>创建时间:{{ item.createTime }}</text>
<text>身份证:{{ item.idCard }}</text>
<text>身份证:{{ item.idCard?.desensitizedValue }}</text>
</view>
</view>
</view>
@ -74,16 +79,15 @@
</view>
</scroll-view>
</view>
</view>
</template>
<script setup lang="ts">
import Taro, {useLoad} from "@tarojs/taro";
import Taro, { useLoad } from '@tarojs/taro'
import './projectDetails.scss'
import {ref} from "vue";
import api from "@/request/index";
import { ref } from 'vue'
import api from '@/request/index'
import dayjs from 'dayjs'
import {ServiceProjectSecurityUserPagerVo} from "@/types/subPages/projectManager/myProject";
import { ServiceProjectSecurityUserPagerVo } from '@/types/subPages/projectManager/myProject'
const serviceProject = ref()
const enterprisesUnitName = ref('')
@ -91,11 +95,10 @@ const securityUserList = ref<ServiceProjectSecurityUserPagerVo[]>([])
useLoad(async (options) => {
enterprisesUnitName.value = options.enterprisesUnitName
serviceProject.value = JSON.parse(options.serviceProject)
console.log(serviceProject.value);
console.log(serviceProject.value)
await projectDetailsTable()
})
const projectDetailsTable = async () => {
// if (total.value === projectData.value.length) return
Taro.showLoading({
title: '加载中',
})
@ -105,10 +108,10 @@ const projectDetailsTable = async () => {
},
page: {
size: 4,
current: current.value
current: current.value,
},
}
}
const resp = await api.post<PagerVo<ServiceProjectSecurityUserPagerVo>>('/miniProgramUser/securityUserPager', queryParams)
const resp = await api.post<PagerVo<ServiceProjectSecurityUserPagerVo>>('/mp/user/securityUserPager', queryParams)
securityUserList.value = [...securityUserList.value, ...resp.data!.records]
total.value = resp.data!.total
isRefresher.value = false

View File

@ -1,10 +1,8 @@
page {
background-color: #f0f0f0;
}
.myProject {
height: 100vh;
overflow: hidden;
background: #f1f1f1;
.myProjectItem {
//height: 20%;
margin: 20px;
border-radius: 10px;
background: #ffffff;
@ -18,16 +16,8 @@
}
.project {
display: flex;
justify-content: space-between;
flex-flow: wrap;
text-align: center;
view {
width: 45%;
height: 100rpx;
border: 1px solid #cccccc;
line-height: 100rpx;
width: 100%;
margin: 8px 0 8px 0;
}
}

View File

@ -4,6 +4,7 @@
<view class="myProjectItem" v-for="(item,index) in myProjectList" :key="index">
<view style="display: flex;justify-content: space-between">
<text>{{ item?.name }}</text>
<text>单位类型{{ item?.type.label }}</text>
</view>
<view class="myProjectIndex">地址
<text>{{ item?.provinceName }}{{ item.cityName }}{{ item.districtsName }}{{ item.streetName }}</text>
@ -13,9 +14,17 @@
<text>电话{{ item?.contactPersonInfo.telephone }}</text>
</view>
<view class="project">
<view @click="projectClick(item.name,serviceProject)"
v-for="(serviceProject,index) in item.serviceProjectList" :key="index">
{{ serviceProject.name }}
<view @click="projectClick(item?.name, serviceProject)" v-for="(serviceProject, index) in item.serviceProjectList" :key="index">
<view style="border: 1px solid #cccccc;color: #9b9b9f">
<view style="display: flex; justify-content: space-between">
<text>项目名称{{serviceProject.name}}</text>
<text>项目类型{{serviceProject.type.label}}</text>
</view>
<view style="display: flex; justify-content: space-between">
<text>项目负责人{{serviceProject.projectManagerMiniProgramUserInfo?.name?serviceProject.projectManagerMiniProgramUserInfo?.name:'无分配项目经理'}}</text>
</view>
<view>责任单位{{serviceProject.securityUnitName}}</view>
</view>
</view>
</view>
</view>
@ -37,10 +46,9 @@ import './myProject.scss'
import {MyProjectList, ServiceProjectList} from "@/types/subPages/projectManager/myProject";
const myProjectList = ref<MyProjectList[]>()
const number = ref(0)
const getMyServiceProject = async () => {
const resp = await api.get<MyProjectList[]>(`/projectManageIndex/getMyServiceProject`)
const resp = await api.get<MyProjectList[]>(`/mp/pmi/get_my_sp`)
myProjectList.value = resp.data
number.value = (resp.data?.length || 0)
}

View File

@ -5,7 +5,7 @@
<view style="display: flex;justify-content: space-between">
<text style="font-size: 18px">{{ enterprisesUnitName }}-----{{ serviceProject?.name }}
</text>
<text>进行中</text>
<!-- <text>进行中</text>-->
</view>
<view>
<view style="float: left;width: 50%;" class="content">
@ -62,12 +62,12 @@
<text>职位:{{ item.workPost ? item.workPost : '无' }}</text>
</view>
<view style="display: flex;justify-content: space-between">
<text>保安证件:{{ item.securityNumber ? item.securityNumber : '125241256451' }}</text>
<text>保安证件:{{ item.securityNumber ? item.securityNumber : '' }}</text>
<text>出生年月:{{ dayjs(item.dateOfBirth).format('YYYY-MM-DD') }}</text>
</view>
<view style="display: flex;justify-content: space-between">
<text>创建时间:{{ item.createTime }}</text>
<text>身份证:{{ item.idCard }}</text>
<text>身份证:{{ item.idCard?.desensitizedValue }}</text>
</view>
</view>
<view class="projectDetailsTableIndex">
@ -169,7 +169,7 @@ const projectDetailsTable = async () => {
current: current.value
}
}
const resp = await api.post<PagerVo<ServiceProjectSecurityUserPagerVo>>('/miniProgramUser/securityUserPager', queryParams)
const resp = await api.post<PagerVo<ServiceProjectSecurityUserPagerVo>>('/mp/user/securityUserPager', queryParams)
securityUserList.value = [...securityUserList.value, ...resp.data!.records]
total.value = resp.data!.total
isRefresher.value = false
@ -189,7 +189,9 @@ const formAdd = () => {
securityNumber: '',
remark: '',
homeAddress: '',
telephone: ''
telephone: '',
photo:'',
noSecurityNumberDesc:''
} as SecurityUserFormParams
Taro.navigateTo({url: `/subPages/projectManager/securityUserForm/securityUserForm?securityUser=${JSON.stringify(params)}&&type=formInput`})
}
@ -228,7 +230,7 @@ const deleteUssrID = (snowFlakeId: string) => {
}
//
const dialogOk = async () => {
await api.delete(`/projectManageIndex/deleteSecurityUserByServiceProjectId`, {securityUserId: securityUserId.value})
await api.delete(`/mp/user/del_security_user_id`, {securityUserId: securityUserId.value})
initServiceProjectSecurityUserList()
}
//
@ -237,6 +239,7 @@ const detail = (item: ServiceProjectSecurityUserPagerVo) => {
securityUserDetail.value = item
}
const securityUserEdit = (item: ServiceProjectSecurityUserPagerVo) => {
console.log(item)
const params = {...item, sex: item.sex.value}
Taro.navigateTo({url: `/subPages/projectManager/securityUserForm/securityUserForm?securityUser=${JSON.stringify(params)}&type=formInput`})
}
@ -248,7 +251,7 @@ const generateMiniProgramQRCode = async () => {
width: 200,
}
qrcodeVisible.value = true
const resp = await api.get('/projectManageIndex/shareForm_QR_Code', paramsData, {
const resp = await api.get('/wx/qrCode', paramsData, {
header: {
"content-type": 'application/x-www-form-urlencoded'
},

View File

@ -1,8 +1,20 @@
.form {
.formButton {
//position: fixed;
bottom: 60px;
display: flex;
margin-top: auto;
justify-content: space-around;
margin-bottom: 30px
-webkit-justify-content: space-around;
margin-bottom: 10rpx;
right: 0;
width: 100%;
height: 140px
}
}
.uploadPictures{
display: flex;
justify-content: center;
align-items: center;
width: 220px;
height: 254px;
border: 2px solid #d5d3d3;
}

View File

@ -1,30 +1,40 @@
<template>
<view class="form">
<nut-form ref="formRef" :model-value="formData" :rules="rules">
<nut-form-item label="头像" prop="avatar">
<!-- <view class="uploadPictures" @click="chooseImage">-->
<!-- <view v-if="!formData.photo">-->
<!-- <IconFont name="uploader" size="25" color="#98a7b0"></IconFont>-->
<!-- </view>-->
<!-- <image v-else :src="minioBaseUrl +formData.photo" style="width: 100%; height: 100%"></image>-->
<!-- </view>-->
<view @click="chooseImage" size>
<image v-if="!formData.photo" src="@/assets/logo/avatar1.png" style="width: 50px; height: 50px"></image>
<image v-else :src="minioBaseUrl +formData.photo" style="width: 160px; height: 128px"></image>
</view>
</nut-form-item>
<nut-form-item label="身份证" prop="idCard">
<view style="display: flex; justify-content: space-between; align-items: center">
<nut-input v-model="formData.idCard" placeholder="请填写身份证" type="text" @blur="cardBlur" />
<view style="color: #3a6bbe; width: 70px; text-align: center" @click="idCardBlur(formData, 0)">查询</view>
</view>
</nut-form-item>
<nut-form-item label="姓名" prop="name">
<nut-input v-model="formData.name" placeholder="请输入姓名" type="text" />
</nut-form-item>
<nut-form-item label="性别" prop="sex">
<nut-radio-group v-model="formData.sex" direction="horizontal">
<nut-radio v-for="item in SEX" :key="item.value" :label="item.value"
>{{ item.label }}
</nut-radio>
<nut-radio v-for="item in SEX" :key="item.value" :label="item.value">{{ item.label }} </nut-radio>
</nut-radio-group>
</nut-form-item>
<nut-form-item label="身份证" prop="idCard">
<nut-input v-model="formData.idCard" placeholder="请填写身份证" type="text" @blur="idCardBlur"/>
</nut-form-item>
<nut-form-item label="出生日期" prop="dateOfBirth">
<view @click="showPicker = true">
{{
formData.dateOfBirth ? dayjs(formData.dateOfBirth).format('YYYY-MM-DD') : '请选择出生年月'
}}
{{ formData.dateOfBirth ? dayjs(formData.dateOfBirth).format('YYYY-MM-DD') : '请选择出生年月' }}
</view>
</nut-form-item>
<nut-form-item label="工作岗位" prop="workPost">
<nut-input v-model="formData.workPost" placeholder="请输入工作岗位" type="text" />
</nut-form-item>
<nut-form-item label="手机号" prop="telephone">
<nut-input v-model="formData.telephone" placeholder="请输入手机号" type="text" />
</nut-form-item>
@ -34,16 +44,19 @@
<nut-form-item label="地址" prop="homeAddress">
<nut-input v-model="formData.homeAddress" placeholder="请输入地址" type="text" />
</nut-form-item>
<nut-form-item label="保安证号" prop="securityNumber">
<nut-form-item label="保安证号" prop="securityNumber">
<nut-input v-model="formData.securityNumber" placeholder="请输入保安证号" type="text" />
</nut-form-item>
<nut-form-item label="无证说明" v-if="!formData.securityNumber">
<nut-input v-model="formData.noSecurityNumberDesc" placeholder="无证说明" type="text" />
</nut-form-item>
<nut-form-item label="备注" prop="remark">
<nut-input v-model="formData.remark" placeholder="请填写备注" type="text" />
</nut-form-item>
</nut-form>
<view class="formButton">
<nut-button style="width: 45%" type="success" size="small" @click="submit">提交</nut-button>
<nut-button style="width: 45%" size="small" @click="formRef?.reset()">重置表单</nut-button>
<nut-button style="width: 45%" size="small" @click="reset">重置表单</nut-button>
</view>
<nut-popup v-model:visible="showPicker" position="bottom">
<nut-date-picker
@ -55,81 +68,176 @@
@cancel="showPicker = false"
></nut-date-picker>
</nut-popup>
<nut-dialog content="详情" v-model:visible="visible" @ok="onOk" @cancel="cancel">
<slot>
<view style="margin-bottom: 5px">
<view>姓名{{ securityNumberByIdCard?.name ? securityNumberByIdCard?.name : '无' }}</view>
<view>保安证件号{{ securityNumberByIdCard?.bayzh ? securityNumberByIdCard?.bayzh : '无' }}</view>
<view>身份证{{ securityNumberByIdCard?.sfzhm ? securityNumberByIdCard?.sfzhm : '无' }}</view>
</view>
</slot>
</nut-dialog>
</view>
</template>
<script setup lang="ts">
import {ref} from "vue";
import {enumSelectNodes} from "@/enums";
import {FormRules} from "@nutui/nutui-taro/dist/types/__VUE/form/types";
import api from "@/request";
import { ref } from 'vue'
import { enumSelectNodes } from '@/enums'
import { FormRules } from '@nutui/nutui-taro/dist/types/__VUE/form/types'
import api from '@/request'
import './securityUserForm.scss'
import Taro, {useLoad} from "@tarojs/taro";
import dayjs from "dayjs";
import {SecurityUserFormParams} from "@/types/subPages/projectManager/securityUserForm";
import {FormInstance} from "@nutui/nutui-taro";
import Taro, { useLoad } from '@tarojs/taro'
import dayjs from 'dayjs'
import { SecurityUserFormParams, securityNumberByIdCard } from '@/types/subPages/projectManager/securityUserForm'
import { FormInstance } from '@nutui/nutui-taro'
import { generateSimpleObjectName, getResignedObjectUrl } from '@/utils'
import {IconFont} from "@nutui/icons-vue-taro";
const SEX = enumSelectNodes('Sex')
const minioBaseUrl = process.env.TARO_APP_MINIO_URL
const showPicker = ref(false)
const type = ref<'formInput' | 'QcCodeInput'>(null!);
const Url = ref('')
const type = ref<'formInput' | 'QcCodeInput'>(null!)
const formData = ref<SecurityUserFormParams>({} as any)
const formRef = ref<FormInstance>(null!)
const visible = ref<boolean>(false)
const securityNumberByIdCard = ref<securityNumberByIdCard>()
const uploadRef = ref<any>(null)
const modelValue = ref('')
const rules: FormRules = {
name: [
{required: true, message: "请输入姓名"},
],
sex: [{required: true, message: "请选择性别"}],
name: [{ required: true, message: '请输入姓名' }],
sex: [{ required: true, message: '请选择性别' }],
idCard: [
{required: true, message: "请输入身份证号"},
{ required: true, message: '请输入身份证号' },
{
regex: /^(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))((0[1-9])|([12][0-9])|(30|31))\d{3}(\d|X)$)/,
message: "身份证格式错误",
message: '身份证格式错误',
},
],
telephone: [
{required: true, message: "请输入手机号"},
]
};
telephone: [{ required: true, message: '请输入手机号' }],
}
useLoad((options) => {
type.value = options.type;
type.value = options.type
if (type.value === 'QcCodeInput') {
formData.value = {
name: '',
serviceProjectId: options.pid,
securityUnitId: options.uid,
sex: 0,
idCard: '',
dateOfBirth: null
idCard: null,
telephone: null,
dateOfBirth: null,
noSecurityNumberDesc: options.noSecurityNumberDesc,
photo: '',
}
} else {
formData.value = JSON.parse(options.securityUser)
const form = JSON.parse(options.securityUser)
formData.value = Object.assign({}, form, {
idCard: form.idCard.originalValue,
telephone: form.telephone.originalValue,
photo: form.photo
})
}
})
const idCardBlur = (e: any) => {
const value = e.detail.value
const idCardBlur = async (e: any, num: number) => {
const value = e.idCard
if (value) {
Taro.request({
url: 'https://www.hnjinglian.cn:5678/common/querySecurityNumberByIdCard',
data: {
idCard: value,
},
method: 'GET',
success: ({ data }) => {
visible.value = true
securityNumberByIdCard.value = data.data
},
})
return
} else {
visible.value = false
console.log(value)
}
cardBlur(value, num)
}
const cardBlur = (e, num) => {
let value = ''
if (num === 0) {
value = e
} else {
value = e.detail.value
}
if (!value?.length || value.length < 18) {
formData.value.dateOfBirth = null;
formData.value.dateOfBirth = null
cancel()
return
}
const birthDate = value.substring(6, 14);
const year = birthDate.substring(0, 4);
const month = birthDate.substring(4, 6);
const day = birthDate.substring(6, 8);
const birthDate = value.substring(6, 14)
const year = birthDate.substring(0, 4)
const month = birthDate.substring(4, 6)
const day = birthDate.substring(6, 8)
formData.value.dateOfBirth = new Date(parseInt(year), parseInt(month) - 1, parseInt(day))
}
const onOk = () => {
formData.value.securityNumber = securityNumberByIdCard.value?.bayzh
formData.value.name = securityNumberByIdCard.value?.name
}
const cancel = () => {
formData.value.securityNumber = ''
formData.value.name = ''
visible.value = false
}
const chooseImage = () => {
Taro.chooseMedia({
count: 1, //
mediaType: ['image', 'video'],
sourceType: ['album', 'camera'],
sizeType:['original', 'compressed'],
camera: 'front',
success: async (res) => {
Url.value = res.tempFiles[0].tempFilePath
const objectName = generateSimpleObjectName(Url.value, '/securityUser')
const uploadUrl = await getResignedObjectUrl(process.env.TARO_APP_MINIO_BUCKET, objectName)
modelValue.value = '/' + process.env.TARO_APP_MINIO_BUCKET + objectName
// 使 wx.getFileSystemManager().readFileSync
const fs = Taro.getFileSystemManager()
const fileData = fs.readFileSync(Url.value) //
// PUT
Taro.request({
url: uploadUrl, //
method: 'PUT',
header: {
'Content-Type': 'application/octet-stream', //
},
data: fileData, //
success(res) {
formData.value.photo = modelValue.value
console.log('上传成功', res)
},
fail(err) {
console.error('上传失败', err)
},
})
},
fail(err) {
console.error('选择文件失败', err)
},
})
}
const submit = () => {
formData.value.photo = modelValue.value
formRef.value?.validate().then(async ({ valid }) => {
if (valid) {
let url: string;
let url: string
if (type.value === 'formInput') {
url = '/projectManageIndex/saveOrUpdateSecurityUser'
url = '/mp/user/add_security_user_upd'
} else {
url = '/miniProgramUser/qrCodeFormInputSecurityUser'
url = '/mp/user/qrCodeFormInputSecurityUser'
}
const resp = await api.post(url, formData.value)
Taro.showToast({
@ -144,16 +252,23 @@ const submit = () => {
securityUnitId: formData.value.securityUnitId,
name: '',
workPost: '',
telephone: '',
telephone: null,
sex: 0,
nativePlace: '',
idCard: '',
idCard: null,
dateOfBirth: null,
securityNumber: '',
remark: '',
homeAddress: ''
homeAddress: '',
noSecurityNumberDesc: '',
photo: '',
}
uploadRef.value?.clearUploadQueue()
}
})
}
const reset = () => {
formRef.value?.reset()
}
</script>

View File

@ -0,0 +1,26 @@
.userinform {
height: 100%;
width: 100%;
}
.userItem {
color: #7b7b7b;
font-size: 14px;
margin-bottom: 8px;
.textIndex {
margin-left: 20rpx;
}
}
.text {
width: 96%;
}
.singleChoice {
width: 90%;
}
.label {
margin: 0 20px;
}

View File

@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '',
})

View File

@ -0,0 +1,121 @@
<template>
<view class="userinform">
<view class="userItem">
<!-- v-model="item.snowFlakeId" 展开所有 -->
<nut-collapse v-model="item.snowFlakeId" accordion v-for="(item, index) in starRating[findIndex].itemList" :key="item.snowFlakeId">
<nut-collapse-item :name="item.snowFlakeId" :title="item.name">
<view class="singleChoice">
<nut-radio-group v-if="item.type.value === 'radio'" v-model="item.selectedID" @change="(modelValue) => onChange(modelValue, item.name)">
<nut-radio v-for="(items, indexs) in item.standardList" size="40" :label="items.snowFlakeId" :key="indexs"> {{ items.name }}</nut-radio>
</nut-radio-group>
<!-- 不能直接去更改 v-model 绑定的 这个数据 ,否则会造成无限递归-->
<nut-checkbox-group v-else v-model="item.selectedGroup" @change="(arr) => checkboxGroupChange(arr, index)" style="display: flex; flex-direction: column">
<nut-checkbox
v-model="items.isSelected"
@change="(state, label) => checkboxChange(state, label, index, i)"
v-for="(items, i) in item.standardList"
size="40"
:label="items.snowFlakeId"
:key="i"
style="margin-bottom: 20rpx"
shape="button"
>{{ items.name}}</nut-checkbox
>
</nut-checkbox-group>
</view>
</nut-collapse-item>
</nut-collapse>
</view>
</view>
</template>
<script setup>
import Taro, { useLoad, useUnload } from '@tarojs/taro'
import { ref, computed, watch, nextTick } from 'vue'
import { useDailyStore } from '@/store/daily.ts'
const store = useDailyStore()
const starRating = ref([])
const findIndex = ref(0)
const airdefenceEnumdata = ref([])
useLoad((options) => {
Taro.setNavigationBarTitle({
title: options.name,
})
findIndex.value = options.index
})
const daily = computed(() => store.getdailyinspection)
watch(
daily,
(newData) => {
starRating.value = newData
},
{ immediate: true }
)
/**
* @assessmentRecordDetails 选择后的数据用于提交
*/
const assessmentRecordDetails = ref([])
const onChange = (modelValue, name) => {
starRating.value[findIndex.value].itemList.forEach((element) => {
if (name === element.name) {
element.selectedID = modelValue
element.standardList.forEach((item) => {
if (item.snowFlakeId == modelValue) {
element.selected_points = item.deductionPoints //
}
})
}
})
}
const checkboxGroupChange = function (arr, index) {
let points = 0
starRating.value[findIndex.value].itemList.forEach((element, i) => {
if (i === index) {
element.selectedGroup.forEach((selectedId) => {
element.standardList.forEach((standardItem) => {
if (selectedId === standardItem.snowFlakeId) {
points += standardItem.deductionPoints
}
})
})
}
})
starRating.value[findIndex.value].itemList[index].selected_points = points
}
const checkboxChange = async function (state, label, index, i) {
// DOM
await nextTick() // DOM
// DOM
let arr
if (label === '达标' && i === 0) {
arr = ['null']
} else {
arr = starRating.value[findIndex.value].itemList[index].selectedGroup.filter((selectedId) => selectedId != 'null')
}
if (arr?.length === 0) {
arr = ['null']
}
if (JSON.stringify(arr) !== JSON.stringify(starRating.value[findIndex.value].itemList[index].selectedGroup)) {
starRating.value[findIndex.value].itemList[index].selectedGroup = arr
}
}
useUnload(() => {
let points = 0
starRating.value[findIndex.value].itemList.forEach((element) => {
points += element.selected_points
})
starRating.value[findIndex.value].currentScore = points
store.dailyinspectionList([...starRating.value])
})
</script>

View File

@ -0,0 +1,65 @@
.sign-box {
position: fixed;
z-index: 100;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.fixedIcon {
z-index: 1000;
position: fixed;
right: 20px;
top: 20px;
height: 100rpx;
width: 100rpx;
}
.sign-view {
height: 100%;
}
.sigh_btns_false {
position: absolute;
bottom: 15rpx;
right: 0;
width: 100%;
height: 100rpx;
display: flex;
justify-content: space-evenly;
align-items: center;
}
.sigh_btns {
transform: rotate(90deg);
transform-origin: center
}
.sigh_btns_noRotate {}
.sigh_btns_true {
position: absolute;
left: 0;
top: 0;
width: 20%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-evenly;
align-items: center;
}
.mycanvas {
width: 100%;
background-color: #ececec;
}
.canvsborder {
border: 1rpx solid #333;
position: fixed;
top: 0;
left: 10000rpx;
}

View File

@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '签名',
disableScroll: true
})

View File

@ -0,0 +1,174 @@
<template>
<!-- 签字功能 -->
<view catchtouchmove="true">
<view class="sign-box">
<view class="fixedIcon" @click.stop="changeDirection">
<image style="height: 100rpx; width: 100rpx" :src="icon"></image>
</view>
<canvas class="mycanvas" :style="{ height: height + 'px' }" canvas-id="mycanvas" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend"></canvas>
<canvas canvas-id="camCacnvs" :style="{ height: width + 'px' }" class="canvsborder"></canvas>
<view :class="direction ? 'sigh_btns_true' : 'sigh_btns_false'">
<nut-button :class="direction ? 'sigh_btns' : 'sigh_btns_noRotate'" style="height: 60rpx" shape="square" type="info" @click="handleCancel"> 取消 </nut-button>
<nut-button :class="direction ? 'sigh_btns' : 'sigh_btns_noRotate'" style="height: 60rpx" shape="square" type="info" @click="handleReset"> 重写 </nut-button>
<nut-button :class="direction ? 'sigh_btns' : 'sigh_btns_noRotate'" style="height: 60rpx" shape="square" type="info" @click="handleConfirm"> 确认 </nut-button>
</view>
</view>
</view>
</template>
<script setup>
import './signature.scss'
import Taro, { useLoad, useUnload } from '@tarojs/taro'
import { ref, onMounted, watch, computed } from 'vue'
import icon from '@/assets/images/rotate1.png'
const _index = ref(0)
import { useDailyStore } from '@/store/daily'
const store = useDailyStore()
const width = ref(0)
const height = ref(300)
const points = ref([]) //
const tempPoint = ref([]) //
let that
let id
let type
useLoad((options) => {
_index.value = Number(options.index)
Taro.setNavigationBarTitle({
title: options.name,
})
})
const ctx = ref(null)
onMounted(() => {
that = this
const { windowWidth, windowHeight } = Taro.getSystemInfoSync()
width.value = windowWidth
height.value = windowHeight * 0.98 //
//
ctx.value = Taro.createCanvasContext('mycanvas', this)
ctx.value.lineWidth = 2
ctx.value.lineCap = 'round'
ctx.value.lineJoin = 'round'
})
const touchstart = (e) => {
const startX = e.changedTouches[0].x
const startY = e.changedTouches[0].y
points.value.push({ X: startX, Y: startY })
ctx.value.beginPath()
}
const touchmove = (e) => {
const moveX = e.changedTouches[0].x
const moveY = e.changedTouches[0].y
points.value.push({ X: moveX, Y: moveY })
if (points.value.length >= 2) {
draw()
}
tempPoint.value.push({ X: moveX, Y: moveY })
}
const touchend = () => {
points.value = []
}
const draw = () => {
const point1 = points.value[0]
const point2 = points.value[1]
points.value.shift()
ctx.value.moveTo(point1.X, point1.Y)
ctx.value.lineTo(point2.X, point2.Y)
ctx.value.stroke()
ctx.value.draw(true)
}
const handleCancel = () => {
Taro.navigateBack({ delta: 1 })
}
const handleReset = () => {
ctx.value.clearRect(0, 0, width.value, height.value)
ctx.value.draw(true)
tempPoint.value = []
}
const handleConfirm = () => {
if (tempPoint.value.length === 0) {
Taro.showToast({ title: '请先签名', icon: 'none', duration: 2000 })
return
}
Taro.canvasToTempFilePath({
canvasId: 'mycanvas',
success: (res) => {
console.log('canvasToTempFilePath______________', res)
const tempPath = res.tempFilePath
const ctx = Taro.createCanvasContext('camCacnvs', that)
const scale = 0.5
var targetWidth
var targetHeight
// ___________________________________________________________________
if (direction.value) {
targetWidth = height.value * scale
targetHeight = width.value * scale
//
const angle = (270 * Math.PI) / 180 // 270
const rotatedWidth = Math.abs(width.value * Math.cos(angle)) + Math.abs(height.value * Math.sin(angle))
const rotatedHeight = Math.abs(height.value * Math.cos(angle)) + Math.abs(width.value * Math.sin(angle))
//
const offsetX = 0 // 0
const offsetY = rotatedHeight - targetHeight // y
//
ctx.translate(offsetX, offsetY)
//
ctx.rotate(angle)
ctx.drawImage(tempPath, 0, 0, targetHeight, targetWidth)
} else {
targetWidth = width.value * scale
targetHeight = height.value * scale
ctx.drawImage(tempPath, 0, 0, targetWidth, targetHeight)
}
ctx.draw(false, () => {
Taro.canvasToTempFilePath({
canvasId: 'camCacnvs',
width: targetWidth,
height: targetHeight,
success: (compressedRes) => {
Taro.getFileSystemManager().readFile({
filePath: compressedRes.tempFilePath,
encoding: 'base64',
success: (res) => {
const base64Image = 'data:image/jpeg;base64,' + res.data
console.log('base64Image_________________________', base64Image)
if (_index.value === 1) {
store.change_base64_1(base64Image)
Taro.navigateBack({ delta: 1 })
} else {
store.change_base64_2(base64Image)
Taro.navigateBack({ delta: 1 })
}
},
})
},
})
})
},
})
}
/**
* @direction false 竖屏true 横屏
*/
const direction = ref(true)
const changeDirection = () => {
direction.value = !direction.value
Taro.vibrateShort({ type: 'medium' }).then()
}
</script>

View File

@ -0,0 +1,23 @@
import dayjs from "dayjs";
import api from "@/request";
export const generateSimpleObjectName = (fileName: string, parentDir?: String): string => {
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
let objectName = parentDir + dayjs().format('/YYYY/MM/DD/') + uuid.replace(/-/g, '');
console.log(fileName,objectName,'4444')
if (fileName && fileName.length > 0) {
objectName += fileName.substring(fileName.lastIndexOf('.'))
}
console.log(objectName,'888')
return objectName;
}
export const getResignedObjectUrl = async (bucketName: string, objectName: string): Promise<string> => {
return (await api.get<string>('/common/getResignedObjectUrl', {
bucketName,
objectName
})).data as string;
}

View File

@ -25,6 +25,10 @@ declare namespace NodeJS {
TARO_APP_ID: string
/** 后台服务接口地址 **/
TARO_APP_BASE_API: string
TARO_APP_MINIO_BUCKET:string
TARO_APP_MINIO_URL:string
}
}
@ -102,3 +106,59 @@ interface BaseEnum<T, E = Record<string, any>> {
label: string;
extData: E;
}
interface StandardList {
ckItemId: string
deductionPoints: number
name: string
snowFlakeId: string
isSelected: boolean
}
interface ItemList {
ckGroupId: string
name: string
remark: string
snowFlakeId: string
standardList: StandardList[]
type: { value: string; label: string }
selectedID: string
selected_points: number
selectedGroup: any[]
}
interface StarRating {
itemList: ItemList[]
name: string
remark: string
snowFlakeId: string
totalScore: number
currentScore: number
}
interface UnitEnterprisesUnitList {
label: string
value: string
extData: {
type: {
label: string
value: string
}
}
}
interface CkProjectListByType {
label: string
value: string
extData: {
createTime: string
remark: string
totalScore: number
type: {
label: string
value: string
}
}
}
interface Item {
itemList: any[] // 根据实际情况调整类型
snowFlakeId: string
}

View File

@ -0,0 +1,13 @@
export interface DataStatisticsRes {
/*企事业单位数量 */
enterprisesUnitCount: number;
/*服务项目数量 */
serviceProjectCount: number;
/*保安人员数量 */
securityUserCount: number;
/*无证保安人员数量 */
noCardSecurityUserCount: number;
}

View File

@ -15,6 +15,7 @@ export interface MyProjectList {
securityUnitId: string;
street?: string;
streetName?: string;
type?:any
}
export interface ContactPersonInfo {
@ -30,24 +31,27 @@ export interface ServiceProjectList {
name?: string;
remark?: string;
securityUserTotal?: number;
securityUnitName?:string;
serviceArea?: number;
snowFlakeId?: string;
staffTotal?: number;
type?: string;
type?: any;
projectManagerMiniProgramUserInfo?: ProgramUserInfo
}
export interface ProgramUserInfo {
idCard: null
name: string
telephone: string
idCard?: null
name?: string
telephone?: string
}
export interface ServiceProjectSecurityUserPagerVo {
createTime?: string;
dateOfBirth?: string;
homeAddress?: string;
idCard?: string;
idCard?: {
desensitizedValue?:string
originalValue?:string
};
name?: string;
nativePlace?: string;
remark?: string;
@ -59,3 +63,4 @@ export interface ServiceProjectSecurityUserPagerVo {
workPost?: string;
sex: BaseEnum<number>
}

View File

@ -2,14 +2,27 @@ export interface SecurityUserFormParams {
snowFlakeId?: string;
securityUnitId: string;
serviceProjectId: string;
name?: string;
name: string | undefined;
workPost?: string;
telephone?: string;
telephone: value | null;
sex: number;
nativePlace?: string;
idCard: string;
idCard: value | null;
dateOfBirth?: Date | null;
securityNumber?: string;
photo?:string;
remark?: string;
noSecurityNumberDesc:string;
homeAddress?: string
}
export interface value{
desensitizedValue?:string
originalValue?:string
}
export interface securityNumberByIdCard{
name: string | undefined
bayzh?: string
sfzhm?: string
}

View File

@ -7,7 +7,22 @@ VITE_DROP_CONSOLE=false
VITE_APP_BASE_API=/api
# VITE_APP_PROXY_URL=http://localhost:8765
VITE_APP_PROXY_URL=http://172.10.10.93:8765
#
# rsa 公钥
VITE_APP_RSA_PUBLIC_KEY=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJps/EXxxSpEM1Ix4R0NWIOBciHCr7P7coDT8tNKfelgR7txcJOqHCO/MIWe7T04aHQTcpQxqx9hMca7dbqz8TZpz9jvLzE/6ZonVKxHsoFnNlHMp1/CPAJ9f6D9wYicum2KltJkmQ0g//D9W2zPCYoGOmSRFcZx/KEBa4EM53jQIDAQAB
# 高德 myx
# VITE_APP_GAODE_KEY=ca549d915cb38803582ca7e85c5f972c
# VITE_APP_GAODE_VERSION=2.0
# VITE_APP_SECURITY_JS_CODE=f464462874676b3f1469780a62e5b921
# 高德 lz
VITE_APP_GAODE_KEY=f379a3f860a68d7438526275d6a94b05
VITE_APP_GAODE_VERSION=2.0
VITE_APP_SECURITY_JS_CODE=432125a0f8d8cad2dac38b77d6f6728f
# minio
VITE_APP_MINIO_URL=http://118.253.177.137:9000
VITE_APP_MINIO_BUCKET=police-security-dev

View File

@ -5,4 +5,15 @@ VITE_DROP_CONSOLE=true
# axios
VITE_APP_BASE_API=/api
VITE_APP_PROXY_URL=https://172.10.10.238:8765
VITE_APP_PROXY_URL=http://172.10.10.93:8765
# rsa 公钥
VITE_APP_RSA_PUBLIC_KEY=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCpu1C3JHZ+Ng/eVVCZtwKsOZv9RktpAL13pKy4FoRHyNv2t8TPV2AMzLzfEzlWx001nBxyVxEMR2N9jAcqFLHv7r16ciOzbtzB9dky2G+bc9jIs4/EdVK5bAZcPRh5Jrb78sC9PHyR4AeceDyCIKHLUbWBJB4NTZE0s1Wh5kMynQIDAQAB
# 高德
VITE_APP_GAODE_KEY=f379a3f860a68d7438526275d6a94b05
VITE_APP_GAODE_VERSION=2.0
VITE_APP_SECURITY_JS_CODE=432125a0f8d8cad2dac38b77d6f6728f
# minio
VITE_APP_MINIO_URL=https://www.hnjinglian.cn:9002
VITE_APP_MINIO_BUCKET=police-security

View File

@ -9,6 +9,8 @@ lerna-debug.log*
node_modules
dist
policeManagement
package-lock.json
dist-ssr
*.local

View File

@ -9,7 +9,6 @@ declare module 'vue' {
export interface GlobalComponents {
AAvatar: typeof import('ant-design-vue/es')['Avatar']
AButton: typeof import('ant-design-vue/es')['Button']
ACard: typeof import('ant-design-vue/es')['Card']
ACascader: typeof import('ant-design-vue/es')['Cascader']
ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
ACheckboxGroup: typeof import('ant-design-vue/es')['CheckboxGroup']
@ -17,10 +16,11 @@ declare module 'vue' {
AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
ADatePicker: typeof import('ant-design-vue/es')['DatePicker']
ADivider: typeof import('ant-design-vue/es')['Divider']
ADrawer: typeof import('ant-design-vue/es')['Drawer']
AdministrativeDivisionTree: typeof import('./src/components/tree/AdministrativeDivisionTree.vue')['default']
ADropdown: typeof import('ant-design-vue/es')['Dropdown']
AForm: typeof import('ant-design-vue/es')['Form']
AFormItem: typeof import('ant-design-vue/es')['FormItem']
AImage: typeof import('ant-design-vue/es')['Image']
AInput: typeof import('ant-design-vue/es')['Input']
AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
@ -34,12 +34,11 @@ declare module 'vue' {
APagination: typeof import('ant-design-vue/es')['Pagination']
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
APopover: typeof import('ant-design-vue/es')['Popover']
ARadio: typeof import('ant-design-vue/es')['Radio']
AProgress: typeof import('ant-design-vue/es')['Progress']
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
ARangePicker: typeof import('ant-design-vue/es')['RangePicker']
ARow: typeof import('ant-design-vue/es')['Row']
ASelect: typeof import('ant-design-vue/es')['Select']
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
ASpace: typeof import('ant-design-vue/es')['Space']
ASpin: typeof import('ant-design-vue/es')['Spin']
ASubMenu: typeof import('ant-design-vue/es')['SubMenu']
@ -53,15 +52,14 @@ declare module 'vue' {
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
ATreeSelect: typeof import('ant-design-vue/es')['TreeSelect']
FormProMax: typeof import('./src/components/form/FormProMax.vue')['default']
HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
IconFont: typeof import('./src/components/iconfont/IconFont.vue')['default']
Layout: typeof import('./src/components/layout/layout.vue')['default']
LayoutHeader: typeof import('./src/components/layout/header/LayoutHeader.vue')['default']
MapContainer: typeof import('./src/components/aMap/MapContainer.vue')['default']
MenuItem: typeof import('./src/components/layout/MenuItem.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SingleImageFileUpload: typeof import('./src/components/upload/SingleImageFileUpload.vue')['default']
Sliber: typeof import('./src/components/layout/sliber/sliber.vue')['default']
SystemMenus: typeof import('./src/components/layout/SystemMenus.vue')['default']
TableProMax: typeof import('./src/components/table/TableProMax.vue')['default']
TelephoneLogin: typeof import('./src/components/login/TelephoneLogin.vue')['default']

View File

@ -1,6 +0,0 @@
declare module '*.vue' {
import type { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>
export default component
}

View File

@ -1,101 +0,0 @@
declare const __APP_ENV: ImportMetaEnv;
declare global {
/**
*
*/
interface JsonResult<T> {
code: number;
message: string;
data?: T;
}
export interface SecurityUnitPagerQueryParams {
/** 名称 **/
name?: string;
/** 社会编码 **/
socialCode?: string;
/** 行政区划编码 **/
administrativeDivisionCodes?: string[];
/** 是否启用 **/
isEnable?: number;
/** 审核状态 **/
checkStatus?: number;
}
interface BaseEnum<T> {
value: T;
label: string
}
class TreeNodeVo<T, E = Record<string, any>> {
value: T;
parentValue: T;
label: string;
orderIndex?: number;
children?: TreeNodeVo<T>[]
extData?: E;
}
declare interface Grid {
//栅格占据的列数
span?: number;
//栅格左侧的间隔格数
offset?: number;
//栅格向右移动格数
push?: number;
//栅格向左移动格数
pull?: number;
//<768px 响应式栅格数或者栅格属性对象
xs?: number;
//≥768px 响应式栅格数或者栅格属性对象
sm?: number;
//≥992px 响应式栅格数或者栅格属性对象
md?: number;
//≥1200px 响应式栅格数或者栅格属性对象
lg?: number;
//≥1920px 响应式栅格数或者栅格属性对象
xl?: number;
}
interface dataStatus {
account: string;
password: string;
remark: string;
checkStatus: {
extData: {
color: string;
};
label: string;
value: number;
};
}
class SelectNodeVo<T, E = Record<string, any>> {
value: T;
label: string;
options?: SelectNodeVo<T>[]
orderIndex?: number;
disabled?: boolean;
extData?: E
}
interface ExtData {
color?: string;
}
interface Option {
label: string;
value: string | number;
extData?: ExtData | null;
}
interface OptionsResponse {
IsEnable: Option[];
IsOrNot: Option[];
Sex: Option[];
CheckStatus: Option[];
ServiceProjectType: Option[];
DeleteFlag: Option[];
}
}

View File

@ -1,15 +1,18 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script type="module" src="/src/assets/iconfont/iconfont.js"></script>
<title>Vite + Vue + TS</title>
<title>公安后台</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -6,30 +6,36 @@
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"build": "vite build --mode production",
"preview": "vite preview"
},
"dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1",
"@vueuse/core": "^11.2.0",
"ant-design-vue": "^4.2.3",
"axios": "^1.7.5",
"dayjs": "^1.11.13",
"jsencrypt": "^3.3.2",
"lodash-es": "^4.17.21",
"pinia": "^2.2.2",
"pinia-plugin-persistedstate": "^3.2.0",
"vue-component-type-helpers": "^2.1.2",
"sass": "^1.77.8",
"vue": "^3.4.37",
"vue-component-type-helpers": "^2.1.2",
"vue-router": "4",
"vue-uuid": "^3.0.0"
},
"devDependencies": {
"@amap/amap-jsapi-types": "^0.0.15",
"@types/lodash-es": "^4.17.12",
"@types/node": "^22.5.1",
"@types/react": "^18.3.12",
"@vitejs/plugin-vue": "^5.1.2",
"@vitejs/plugin-vue-jsx": "^4.0.1",
"@vitejs/plugin-vue-jsx": "^4.1.0",
"autoprefixer": "^10.4.20",
"postcss": "^8.4.41",
"tailwindcss": "^3.4.10",
"terser": "^5.36.0",
"typescript": "^5.5.3",
"unplugin-vue-components": "^0.27.4",
"vite": "^5.4.1",

View File

@ -0,0 +1,178 @@
/* 扩展ant design pro按钮组件颜色 */
$--my-antd-important: !important;
.btn-danger {
color: #ffffff;
background-color: #F5222D;
border-color: #F5222D;
&:hover,
&:focus {
color: #ffffff $--my-antd-important;
background-color: #ff4d4f $--my-antd-important;
border-color: #ff4d4f $--my-antd-important;
}
&:active,
&.active {
color: #ffffff $--my-antd-important;
background-color: #cf1322 $--my-antd-important;
border-color: #cf1322 $--my-antd-important;
}
}
.btn-volcano {
color: #ffffff;
background-color: #FA541C;
border-color: #FA541C;
&:hover,
&:focus {
color: #ffffff $--my-antd-important;
background-color: #ff7a45 $--my-antd-important;
border-color: #ff7a45 $--my-antd-important;
}
&:active,
&.active {
color: #ffffff $--my-antd-important;
background-color: #d4380d $--my-antd-important;
border-color: #d4380d $--my-antd-important;
}
}
.btn-warn {
color: #ffffff;
background-color: #FAAD14;
border-color: #FAAD14;
&:hover,
&:focus {
color: #ffffff $--my-antd-important;
background-color: #ffc53d $--my-antd-important;
border-color: #ffc53d $--my-antd-important;
}
&:active,
&.active {
color: #ffffff $--my-antd-important;
background-color: #d48806 $--my-antd-important;
border-color: #d48806 $--my-antd-important;
}
}
.btn-success {
color: #ffffff;
background-color: #52C41A;
border-color: #52C41A;
&:hover,
&:focus {
color: #ffffff $--my-antd-important;
background-color: #73d13d $--my-antd-important;
border-color: #73d13d $--my-antd-important;
}
&:active,
&.active {
color: #ffffff $--my-antd-important;
background-color: #389e0d $--my-antd-important;
border-color: #389e0d $--my-antd-important;
}
}
.button-color-cyan {
color: #ffffff;
background-color: #13C2C2;
border-color: #13C2C2;
&:hover,
&:focus {
color: #ffffff $--my-antd-important;
background-color: #36cfc9 $--my-antd-important;
border-color: #36cfc9 $--my-antd-important;
}
&:active,
&.active {
color: #ffffff $--my-antd-important;
background-color: #08979c $--my-antd-important;
border-color: #08979c $--my-antd-important;
}
}
.btn-daybreak {
color: #ffffff;
background-color: #1890FF;
border-color: #1890FF;
&:hover,
&:focus {
color: #ffffff $--my-antd-important;
background-color: #096dd9 $--my-antd-important;
border-color: #096dd9 $--my-antd-important;
}
&:active,
&.active {
color: #ffffff $--my-antd-important;
background-color: #40a9ff $--my-antd-important;
border-color: #40a9ff $--my-antd-important;
}
}
.button-color-geekblue {
color: #ffffff;
background-color: #2F54EB;
border-color: #2F54EB;
&:hover,
&:focus {
color: #ffffff $--my-antd-important;
background-color: #1d39c4 $--my-antd-important;
border-color: #1d39c4 $--my-antd-important;
}
&:active,
&.active {
color: #ffffff $--my-antd-important;
background-color: #597ef7 $--my-antd-important;
border-color: #597ef7 $--my-antd-important;
}
}
.btn-purple {
color: #ffffff;
background-color: #722ED1;
border-color: #722ED1;
&:hover,
&:focus {
color: #ffffff $--my-antd-important;
background-color: #9254de $--my-antd-important;
border-color: #9254de $--my-antd-important;
}
&:active,
&.active {
color: #ffffff $--my-antd-important;
background-color: #531dab $--my-antd-important;
border-color: #531dab $--my-antd-important;
}
}
.table-row-warn td {
background-color: #fefca6;
}
.table-row-danger td {
background-color: #f79988;
}
.table-row-success td {
background-color: #b6fcbe;
}
.ant-table-summary td {
background: #edeff6;
}

View File

@ -0,0 +1,53 @@
<template>
<div :id="mapId" class="mapContainer">
<slot></slot>
</div>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, shallowRef } from 'vue'
import { initMap } from '@/utils/aMapUtil'
import { MapContainerProps, MapPlugins } from '@/types/components/map/index.ts'
const props = withDefaults(defineProps<MapContainerProps>(), {
plugins: (): MapPlugins[] => {
return []
},
mapOptions: (): AMap.MapOptions => {
return {
// 3D
viewMode: '3D',
//
zoom: 11,
mapStyle: 'amap://styles/darkblue',
}
},
})
const mapId = 'mapContainer'
const map = shallowRef<AMap.Map>(null)
defineExpose({
mapInstance: map,
})
onMounted(() => {
initMap(props.plugins).then((AMap) => {
map.value = new AMap.Map(mapId, props.mapOptions)
props.initCallback && props.initCallback(map.value)
})
})
onUnmounted(() => {
map.value?.destroy()
})
</script>
<style scoped lang="scss">
#mapContainer {
padding: 0;
margin: 0;
width: 100%;
height: 100%;
}
</style>

View File

@ -67,6 +67,7 @@
:allowClear="item.componentsProps?.allowClear ?? true"
:options="item.options"
/>
<administrative-division-tree-comp v-else-if="item.type === 'administrativeDivisionTree'" style="width: 100%" v-model:value="modelValue[field]" v-bind="item.componentsProps" />
<a-range-picker
v-else-if="item.type === 'rangePicker'"
style="width: 100%"
@ -111,10 +112,14 @@
<script setup lang="ts" generic="T extends Record<string,any>">
import { FormInstance } from 'ant-design-vue'
import { ref } from 'vue'
import { defineAsyncComponent, ref } from 'vue'
import { FormExpose } from 'ant-design-vue/es/form/Form'
import { QuestionCircleOutlined } from '@ant-design/icons-vue'
import { FormProMaxItemOptions, FormProMaxItemProps, FormProMaxProps } from '@/types/components/form/index.ts'
import { ComponentProps } from 'vue-component-type-helpers'
import AdministrativeDivisionTree from '@/components/tree/AdministrativeDivisionTree.vue'
const AdministrativeDivisionTreeComp: ComponentProps<typeof AdministrativeDivisionTree> = defineAsyncComponent(() => import('@/components/tree/AdministrativeDivisionTree.vue'))
const modelValue = defineModel<T>('value', {
default: {},
@ -147,7 +152,7 @@ const props = withDefaults(defineProps<FormProMaxProps<T>>(), {
scrollToFirstError: undefined,
validateOnRuleChange: undefined,
})
console.log(props)
const formProMaxRef = ref<FormInstance>(null!)
const getResponsive = (item: FormProMaxItemProps): Grid => {

View File

@ -144,7 +144,7 @@ const props = withDefaults(defineProps<TableProMaxProps<T, P>>(), {
showExpandColumn: undefined,
sticky: undefined,
})
console.log(props)
// console.log(props)
const slots = defineSlots<TableProMaxSlots<T>>()
@ -189,7 +189,7 @@ const {
props.dataCallback,
props.requestError
)
console.log('pageParams', pageParams)
// console.log('pageParams', pageParams)
onMounted(() => props.requestAuto && requestGetTableData(true))

View File

@ -0,0 +1,84 @@
<template>
<a-cascader
v-model:value="modelValue"
:placeholder="placeholder"
:change-on-select="changeOnSelect"
:options="administrativeDivisionTree"
:load-data="loadData"
:allow-clear="allowClear"
/>
</template>
<script setup lang="ts">
import api from "@/axios";
import {onMounted, ref} from "vue";
import {CascaderProps} from "ant-design-vue";
import {isEmpty} from "lodash-es";
withDefaults(defineProps<{
placeholder?: string,
changeOnSelect?: boolean
allowClear?: boolean
}>(), {
placeholder: '请选择行政区划',
changeOnSelect: true,
allowClear: true
})
const modelValue = defineModel('value', {
default: []
})
const administrativeDivisionTree = ref<TreeNodeVo<string>[]>([])
const loadData: CascaderProps['loadData'] = selectedOptions => {
const targetOption = selectedOptions[selectedOptions.length - 1];
targetOption.loading = true;
administrativeDivisionByParentCode(targetOption.value as string).then(data => {
targetOption.loading = false
targetOption.children = data
administrativeDivisionTree.value = [...administrativeDivisionTree.value]
})
}
/**
* 根据父级编码查询行政区划
* @param code
*/
const administrativeDivisionByParentCode = async (code: string = '0'): Promise<TreeNodeVo<string>[]> => {
const resp = await api.get<TreeNodeVo<string>[]>('/common/administrativeDivisionByParentCode', {
parentCode: code
})
//
return resp.data.map(item => {
delete item.children
return item
});
}
onMounted(async () => {
administrativeDivisionTree.value = await administrativeDivisionByParentCode()
if (!isEmpty(modelValue.value)) {
const ps = modelValue.value.map(code => administrativeDivisionByParentCode(code))
Promise.all(ps).then(data => {
let i = 0;
const deepChildren = (treeData: TreeNodeVo<string>[]) => {
treeData.forEach(item => {
if (item.value === modelValue.value[i]) {
item.children = data[i]
i++;
deepChildren(item.children)
}
})
}
deepChildren(administrativeDivisionTree.value)
})
}
})
</script>
<style scoped lang="scss">
</style>

View File

@ -1,8 +1,9 @@
<template>
<div class="simpleUploadDiv">
<a-progress v-if="uploading" type="circle" :percent="percent" />
<a-image height="80%" v-else :src="minioBaseUrl + modelValue" alt="avatar" />
<a-button class="btn-success" @click="selectFile">{{ btnLabel }}</a-button>
<!-- height="80%" width="80%" -->
<a-image v-else :src="minioBaseUrl + modelValue" alt="avatar" />
<a-button style="margin-top: 4px" class="btn-success" @click="selectFile">{{ btnLabel }}</a-button>
<input id="myFileInput" type="file" style="display: none" />
</div>
</template>

105
policeManagement/src/global.d.ts vendored Normal file
View File

@ -0,0 +1,105 @@
// global.d.ts不能出现 export 关键字否则这个文件会失效
declare const __APP_ENV: ImportMetaEnv;
class TreeNodeVo<T, E = Record<string, any>> {
value: T;
parentValue: T;
label: string;
orderIndex?: number;
children?: TreeNodeVo<T>[]
extData?: E;
}
/**
*
*/
interface JsonResult<T> {
code: number;
message: string;
data?: T;
}
interface SecurityUnitPagerQueryParams {
/** 名称 **/
name?: string;
/** 社会编码 **/
socialCode?: string;
/** 行政区划编码 **/
administrativeDivisionCodes?: string[];
/** 是否启用 **/
isEnable?: number;
/** 审核状态 **/
checkStatus?: number;
}
interface BaseEnum<T> {
value: T;
label: string
}
interface Grid {
//栅格占据的列数
span?: number;
//栅格左侧的间隔格数
offset?: number;
//栅格向右移动格数
push?: number;
//栅格向左移动格数
pull?: number;
//<768px 响应式栅格数或者栅格属性对象
xs?: number;
//≥768px 响应式栅格数或者栅格属性对象
sm?: number;
//≥992px 响应式栅格数或者栅格属性对象
md?: number;
//≥1200px 响应式栅格数或者栅格属性对象
lg?: number;
//≥1920px 响应式栅格数或者栅格属性对象
xl?: number;
}
interface dataStatus {
account: string;
password: string;
remark: string;
checkStatus: {
extData: {
color: string;
};
label: string;
value: number;
};
}
class SelectNodeVo<T, E = Record<string, any>> {
value: T;
label: string;
options?: SelectNodeVo<T>[]
orderIndex?: number;
disabled?: boolean;
extData?: E
}
interface ExtData {
color?: string;
}
interface Option {
label: string;
value: string | number;
extData?: ExtData | null;
}
interface OptionsResponse {
IsEnable: Option[];
IsOrNot: Option[];
Sex: Option[];
CheckStatus: Option[];
ServiceProjectType: Option[];
DeleteFlag: Option[];
}
interface TypeEnum<T> {
value: string;
label: string
}

View File

@ -2,18 +2,15 @@ import {createApp} from 'vue'
import App from '@/App.vue'
import '@/reset.css'
import './index.css'
// 公共样式
import '@/assets/scss/common.scss'
// iconfont css
import '@/assets/scss/myAntD.scss'
import "@/assets/iconfont/iconfont.css";
// vue Router
import router from "@/router";
// pinia stores
import pinia from "@/stores";
const vueApp = createApp(App);
import { initEnums } from "@/config/dict.ts";
//高德类型声明文件
import "@amap/amap-jsapi-types";
initEnums()
vueApp
.use(router)

View File

@ -10,7 +10,7 @@ import { ROUTER_WHITE_LIST } from "@/config";
* createWebHashHistory: 路径带# URL SEO
*/
const router = createRouter({
history: createWebHistory(),
history: createWebHistory('/policeManagement/'),
routes: [...staticRouter],
strict: false,
scrollBehavior: () => ({ left: 0, top: 0 })

View File

@ -81,7 +81,21 @@ export const staticRouter: RouteRecordRaw[] =
keepalive: true
},
component: () => import('@/views/query/publicUnit.vue')
// component: () => import('@/views/query/index.tsx')
// component: () => import('@/views/query/EnterprisesUnit')
},
{
path: 'assessment-record', // 这里使用相对路径而不是 '/register'
name: 'assessment-record',
meta: {
title: '考核记录',
keepalive: true
},
component: () => import('@/views/query/assessmentRecord.vue')
}
// {
// path: 'weapp-user', // 这里使用相对路径而不是 '/register'
// name: 'weapp-user',

View File

@ -15,6 +15,7 @@ import {
} from "ant-design-vue";
import { Ref, UnwrapRef, VNode } from "vue";
import { ComponentProps } from "vue-component-type-helpers";
import AdministrativeDivisionTree from "@/components/tree/AdministrativeDivisionTree.vue";
type FormProMaxItemType =
| 'custom'
@ -32,7 +33,8 @@ type FormProMaxItemType =
| 'datePicker'
| 'rangePicker'
| 'timeRangePicker'
| 'timePicker';
| 'timePicker'
| 'administrativeDivisionTree'
interface FormProMaxItemCommonProps extends ComponentProps<typeof FormItem> {
label?: string,
@ -64,6 +66,7 @@ export type FormProMaxItemOptions<T> = {
| FormProMaxItemProps<'rangePicker', ComponentProps<typeof RangePicker>>
| FormProMaxItemProps<'timeRangePicker', ComponentProps<typeof TimeRangePicker>>
| FormProMaxItemProps<'timePicker', ComponentProps<typeof TimePicker>>
| FormProMaxItemProps<'administrativeDivisionTree', ComponentProps<typeof AdministrativeDivisionTree>>
}
export interface FormProMaxProps<T = {}> extends FormProps {

View File

@ -0,0 +1,44 @@
/**
*
* @link https://lbs.amap.com/api/javascript-api-v2/guide/abc/plugins-list
*/
export type MapPlugins =
'AMap.ElasticMarker'
| 'AMap.ToolBar'
| 'AMap.Scale'
| 'AMap.HawkEye'
| 'AMap.ControlBar'
| 'AMap.MapType'
| 'AMap.Geolocation'
| 'AMap.AutoComplete'
| 'AMap.PlaceSearch'
| 'AMap.DistrictSearch'
| 'AMap.LineSearch'
| 'AMap.StationSearch'
| 'AMap.Driving'
| 'AMap.TruckDriving'
| 'AMap.Transfer'
| 'AMap.Walking'
| 'AMap.Riding'
| 'AMap.DragRoute'
| 'AMap.Geocoder'
| 'AMap.CitySearch'
| 'AMap.IndoorMap'
| 'AMap.MouseTool'
| 'AMap.CircleEditor'
| 'AMap.PolygonEditor'
| 'AMap.PolylineEditor'
| 'AMap.RectangleEditor'
| 'AMap.EllipseEditor'
| 'AMap.BezierCurveEditor'
| 'AMap.MarkerCluster'
| 'AMap.RangingTool'
| 'AMap.CloudDataSearch'
| 'AMap.Weather'
| 'AMap.HeatMap'
export interface MapContainerProps {
plugins?: MapPlugins[],
initCallback?: (map: AMap.Map) => void,
mapOptions?: AMap.MapOptions
}

View File

@ -0,0 +1,61 @@
import {BaseTableRowRecord} from "@/types/components/table";
import {BaseEnum} from "../../../global";
export interface AssessmentRecordPagerVo extends BaseTableRowRecord {
/** 企事业单位名称 **/
enterprisesUnitName: string;
/** 考核项目名称 **/
ckProjectName: string;
/** 考核项目总分 **/
totalScore: number;
/** 考核项目类型 **/
type: BaseEnum<string>;
/** 考核项目备注 **/
ckProjectRemark: string;
/** 公安单位名称 **/
policeUnitName: string;
/** 考核人员签字 **/
assessmentUserSignature: string;
/** 被考核单位人员签字 **/
byAssessmentEnterprisesUnitUserSignature: string;
/** 考核备注 **/
remark: string;
/** 总扣分 **/
deductionPointsTotal: number;
}
export interface AssessmentRecordPagerQueryParams {
type: string
}
export interface DeductedDetailRes {
/*考核分组id */
ckGroupId: number;
groupRowSpan: number;
/*考核分组名字 */
groupName: string;
/*考核分组总分 */
groupTotalScore: number;
/*考核分组备注 */
groupRemark: string;
/*考核项id */
ckItemId: number;
itemRowSpan: number;
/*考核项名字 */
itemName: string;
/*组件类型,可用值:RADIO,MULTIPLE */
itemType: BaseEnum<string>;
/*考核项备注 */
itemRemark: string;
/*考核标准id */
ckStandardId: number;
/*考核标准 */
standardName: string;
/*扣分数 */
deductionPoints: Record<string, unknown>;
/*是否选中 */
isSelected: boolean;
}

View File

@ -0,0 +1,24 @@
import { BaseTableRowRecord } from "@/types/components/table";
export interface serviceProjectSaveOrUpdateParams_ extends BaseTableRowRecord {
snowFlakeId: string
enterprisesUnitId: string,
enterprisesUnitName: string,
projectManagerMiniProgramUserId: string,
projectManagerMiniProgramUserName: string,
name: string,
type: TypeEnum<string>,
twoType: BaseEnum<any>,
outsourceName: string,
isFiling: BaseEnum<number>,
idNumber: string,
serviceArea: number,
buildingTotal: number,
houseTotal: number,
staffTotal: number,
securityUserTotal: number,
remark: string,
createUserName: string,
createTime: string,
enterprisesUnitAdministrativeDivisionCodes: Record<string, any>
}

View File

@ -0,0 +1,160 @@
import { BaseTableRowRecord } from "@/types/components/table";
export interface PoliceUnitPagerQueryParams {
/** 名称 **/
name?: string;
/** 代码 **/
code?: string;
/** 行政区划 **/
administrativeDivisionCodes?: string[];
/** 是否启用 **/
isEnable?: BaseEnum<number>;
/** 审核状态 **/
checkStatus?: BaseEnum<number>;
}
export interface PoliceUnitPagerVo extends BaseTableRowRecord {
sex: { label: string }
/** 名称 **/
name?: string;
/** 代码 **/
code?: string;
/** 省编码 **/
province?: string;
/** 省名字 **/
provinceName?: string;
/** 市编码 **/
city?: string;
/** 市名字 **/
cityName?: string;
/** 区/县编码 **/
districts?: string;
/** 区/县名字 **/
districtsName?: string;
/** 街道编码 **/
street?: string;
/** 街道编码 **/
streetName?: string;
/** 详细地址 **/
address?: string;
/** 联系人 **/
contactPersonInfo?: {
name: string;
telephone: string;
};
/** 是否启用 **/
isEnable?: BaseEnum<number>;
/** 审核状态 **/
checkStatus?: BaseEnum<number>;
}
export interface EnterprisesUnitPagerQueryParams {
/** 公安单位id **/
policeUnitId: string;
}
export interface EnterprisesUnitPagerVo extends BaseTableRowRecord {
sex: { label: string }
/** 名字 **/
name?: string;
type: BaseEnum<string>
/** 公安单位id **/
policeUnitId: string;
/** 省编码 **/
province?: string;
/** 省名称 **/
provinceName?: string;
/** 市编码 **/
city?: string;
/** 市名称 **/
cityName?: string;
/** 区编码 **/
districts?: string;
/** 区名称 **/
districtsName?: string;
/** 街编码 **/
street?: string;
/** 街名称 **/
streetName?: string;
/** 地址 **/
address?: string;
point: [number, number]
/** 联系方式 **/
contactPersonInfo?: {
name: string;
telephone: string;
};
/** 备注 **/
remark?: string;
}
export interface EnterprisesUnitSaveOrUpdateParams {
/** id **/
snowFlakeId?: string;
/** 公安单位id **/
policeUnitId: string;
/** 名称 **/
name: string;
/** 类型 **/
type: string;
/** 行政区划编码 **/
administrativeDivisionCodes: string[];
/** 详细地址 **/
address?: string;
point?: [number, number]
/** 联系人 **/
contactPersonInfo?: {
name: string;
telephone: string;
};
/** 备注 **/
remark?: string;
}
export interface securityUnitIdListParams {
snowFlakeId?: string;
serviceProjectId?: string;
securityUnitId?: string;
name?: string;
remark?: string;
photo?: string;
telephone?: string;
workPost?: string;
sex?: string;
nativePlace?: string;
idCard?: string;
dateOfBirth?: string;
securityNumber?: string;
noSecurityNumberDesc?: string;
homeAddress?: string;
}
export interface securityUnitIdListPagerVo {
snowFlakeId?: string;
serviceProjectId?: string;
securityUnitId?: string;
name?: string;
remark?: string;
photo?: string;
telephone?: {
desensitizedValue?: string;
originalValue?: string;
};
workPost?: string;
sex?: {
label?: string;
value: number | string
};
nativePlace?: string;
idCard?: {
desensitizedValue?: string;
originalValue?: string;
};
dateOfBirth?: string;
securityNumber?: string;
noSecurityNumberDesc?: string;
homeAddress?: string;
}

View File

@ -0,0 +1,18 @@
import AMapLoader from "@amap/amap-jsapi-loader";
import { MapPlugins } from "@/types/components/map/index";
export const initMap = (plugins?: MapPlugins[]): Promise<typeof AMap> => new Promise((resolve, reject) => {
//@ts-ignore
window._AMapSecurityConfig = {
securityJsCode: __APP_ENV.VITE_APP_SECURITY_JS_CODE
}
AMapLoader.load({
key: __APP_ENV.VITE_APP_GAODE_KEY,
version: __APP_ENV.VITE_APP_GAODE_VERSION,
plugins
}).then((aMap: typeof AMap) => {
resolve(aMap)
}).catch(err => {
reject(err)
})
})

View File

@ -1,4 +1,4 @@
<template><div>111111</div></template>
<template><div>公安后台</div></template>
<script setup lang="ts">
defineOptions({

View File

@ -0,0 +1,110 @@
import api from '@/axios'
import { AssessmentRecordPagerVo, DeductedDetailRes } from '@/types/views/assessmentRecord.ts'
import { ColumnsType } from 'ant-design-vue/es/table'
import { Modal, Table } from 'ant-design-vue'
export const deductedDetail = async (assessmentRecord: AssessmentRecordPagerVo) => {
const { data } = await api.get<DeductedDetailRes[]>('/m1/ar/deductedDetail', { assessmentRecordId: assessmentRecord.snowFlakeId })
const groupRowSpan: Record<string, { firstIndex: number; count: number }> = {}
const itemRowSpan: Record<string, { firstIndex: number; count: number }> = {}
data.forEach((item, index) => {
//如果第一次没有值
if (item.ckGroupId) {
if (!groupRowSpan[item.ckGroupId]) {
groupRowSpan[item.ckGroupId] = { count: 1, firstIndex: index }
} else {
groupRowSpan[item.ckGroupId].count++
data[index].groupRowSpan = 0
}
}
if (item.ckItemId) {
if (!itemRowSpan[item.ckItemId]) {
itemRowSpan[item.ckItemId] = { count: 1, firstIndex: index }
} else {
itemRowSpan[item.ckItemId].count++
data[index].itemRowSpan = 0
}
}
})
Object.values(groupRowSpan).forEach(({ count, firstIndex }) => {
data[firstIndex].groupRowSpan = count
})
Object.values(itemRowSpan).forEach(({ count, firstIndex }) => {
data[firstIndex].itemRowSpan = count
})
const ckProjectDetailTableColumns: ColumnsType<DeductedDetailRes> = [
{
dataIndex: 'groupName',
title: '考核分组',
customCell: (_record) => {
return {
rowspan: _record.groupRowSpan,
}
},
customRender: ({ record: _record }) => {
return (
<div>
<p>
{_record.groupName}({_record.groupTotalScore})
</p>
<p>{_record.groupRemark}</p>
</div>
)
},
},
{
dataIndex: 'itemName',
title: '考核项',
customCell: (_record) => {
return {
rowspan: _record.itemRowSpan,
}
},
customRender: ({ record: _record }) => {
if (!_record.ckItemId) {
return '/'
}
return (
<div>
<p>
{_record.itemName}({_record.itemType?.label})
</p>
</div>
)
},
},
{
dataIndex: 'standardName',
title: '标准',
customRender: ({ record: _record }) => {
if (!_record.ckStandardId) {
return '/'
}
return (
<div>
<p style={{ color: _record.isSelected ? 'red' : '' }}>
{_record.standardName}{_record.deductionPoints}
</p>
</div>
)
},
},
]
Modal.info({
title: `${assessmentRecord.enterprisesUnitName}/${assessmentRecord.ckProjectName}】扣分详情`,
icon: ' ',
width: '80%',
centered: true,
content: () => (
<div style={{ height: '80vh', overflow: 'auto' }}>
<Table size='small' bordered pagination={false} class='margin-top-xs' columns={ckProjectDetailTableColumns} data-source={data}></Table>
</div>
),
})
}

View File

@ -0,0 +1,102 @@
<template>
<div>
<TableProMax ref="tableRef" :request-api="reqApi" :columns="columns"> </TableProMax>
</div>
</template>
<script setup lang="tsx">
import TableProMax from '@/components/table/TableProMax.vue'
import api from '@/axios'
import { TableProMaxProps } from '@/types/components/table'
import { AssessmentRecordPagerQueryParams, AssessmentRecordPagerVo } from '@/types/views/assessmentRecord.ts'
import { ComponentExposed } from 'vue-component-type-helpers'
import { ref } from 'vue'
import { Modal } from 'ant-design-vue'
import { deductedDetail } from '@/views/query/assessmentIndex.tsx'
const tableRef = ref<ComponentExposed<typeof TableProMax>>(null!)
type TableProps = TableProMaxProps<AssessmentRecordPagerVo, AssessmentRecordPagerQueryParams>
const reqApi: TableProps['requestApi'] = (params) => api.post('/m1/ar/pager', params) //
const columns: TableProps['columns'] = [
{
dataIndex: 'enterprisesUnitName',
title: '单位名称',
},
{
dataIndex: 'type',
title: '类型',
customRender: ({ text }) => text?.label,
},
{
dataIndex: 'ckProjectName',
title: '考核项目',
},
{
dataIndex: 'totalScore',
title: '总分',
},
{
dataIndex: 'deductionPointsTotal',
title: '扣分',
customRender: ({ record }) => {
if (!record.deductionPointsTotal) {
return <a-tag color='green'>0</a-tag>
}
return (
<a-tag class='pointer' color='red' onClick={() => deductedDetail(record)}>
{record.deductionPointsTotal}
</a-tag>
)
},
},
{
dataIndex: 'result',
title: '得分',
customRender: ({ record }) => record.totalScore - record.deductionPointsTotal,
},
{
dataIndex: 'policeUnitName',
title: '考核单位',
},
{
dataIndex: 'createUserName',
title: '考核人',
},
{
dataIndex: 'createTime',
title: '考核时间',
},
{
dataIndex: 'remark',
title: '考核备注',
},
{
dataIndex: 'signature',
title: '签字',
customRender: ({ record }) => {
return (
<a-button
onClick={() => {
Modal.info({
title: `${record.enterprisesUnitName}${record.ckProjectName} 签字结果`,
content: () => (
<>
<div>
审核人签字: <a-image src={record.assessmentUserSignature} />
</div>
<div>
被审核单位人员签字: <a-image src={record.byAssessmentEnterprisesUnitUserSignature} />
</div>
</>
),
})
}}
>
查看
</a-button>
)
},
},
]
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,365 @@
import dayjs from 'dayjs'
import { TableProMaxProps, TableProMaxSlots } from '@/types/components/table'
import { EnterprisesUnitPagerQueryParams, securityUnitIdListPagerVo, securityUnitIdListParams, PoliceUnitPagerVo } from '@/types/views/unitManage/police/policeUnit.ts'
import { reactive, ref, h } from 'vue'
import { FormExpose } from 'ant-design-vue/es/form/Form'
import { ComponentExposed } from 'vue-component-type-helpers'
import { FormProMaxItemOptions } from '@/types/components/form'
import { dictSelectNodes } from '@/config/dict.ts'
import { Button, message, Modal, Space, Tag, Input } from 'ant-design-vue'
import api from '@/axios'
import TableProMax from '@/components/table/TableProMax.vue'
import { deleteDataModal } from '@/components/tsx/ModalPro.tsx'
import { PageParams } from '@/types/hooks/useTableProMax.ts'
import FormProMax from '@/components/form/FormProMax.vue'
import { debounce } from 'lodash-es'
import { SearchOutlined } from '@ant-design/icons-vue'
import axios from 'axios'
import SingleImageFileUpload from '@/components/upload/SingleImageFileUpload.vue'
type _TableProps = TableProMaxProps<securityUnitIdListPagerVo, EnterprisesUnitPagerQueryParams>
const _formParams = reactive<securityUnitIdListParams>({
snowFlakeId: '', //
serviceProjectId: '', // 服务项目id
securityUnitId: '', // 保安单位id
name: '', //
photo: '',
telephone: '',
workPost: '',
sex: '',
nativePlace: '',
idCard: '',
dateOfBirth: '',
securityNumber: '',
noSecurityNumberDesc: '',
homeAddress: '',
remark: '',
})
const cardBlur = () => {
let value = _formParams.idCard
if (!value?.length || value.length < 18) {
_formParams.dateOfBirth = ''
return
}
const birthDate = value.substring(6, 14)
const year = birthDate.substring(0, 4)
const month = birthDate.substring(4, 6)
const day = birthDate.substring(6, 8)
var _data = new Date(parseInt(year), parseInt(month) - 1, parseInt(day))
_formParams.dateOfBirth = dayjs(_data).format('YYYY-MM-DD HH:mm:ss')
console.log('🚀 ~ cardBlur ~ _data:', _formParams.dateOfBirth)
}
const searchSecurityUnitId = debounce(async () => {
if (process.env.NODE_ENV === 'development') {
console.log('process.env.NODE_ENV === development')
const res = await axios.get(`https://www.hnjinglian.cn:5678/common/querySecurityNumberByIdCard?idCard=${_formParams.idCard}`)
if (res.data?.data?.hasOwnProperty('bayzh')) {
_formParams.securityNumber = res.data.data.bayzh
message.success(res.data.message)
} else {
message.error('未查询到保安证件号')
}
} else {
const res = await api.get<any>('/common/querySecurityNumberByIdCard', { idCard: _formParams.idCard })
console.log(res)
if (res.data?.hasOwnProperty('bayzh')) {
_formParams.securityNumber = res.data.bayzh
message.success(res.message)
} else {
message.error('未查询到保安证件号')
}
}
cardBlur()
}, 300)
const saveOrUpdateEnterprisesUnit = (callback: Function, params, type: string) => {
// console.log('🚀 ~ saveOrUpdateEnterprisesUnit ~ params:', params)
if (type === 'add') {
_formParams.serviceProjectId = params.snowFlakeId
_formParams.securityUnitId = params.securityUnitId
} else {
_formParams.snowFlakeId = params.snowFlakeId
_formParams.serviceProjectId = params.serviceProjectId
_formParams.securityUnitId = params.securityUnitId
_formParams.name = params.name
_formParams.photo = params?.photo
_formParams.telephone = params.telephone.originalValue
_formParams.workPost = params.workPost
_formParams.sex = params.sex.value
_formParams.nativePlace = params.nativePlace
_formParams.idCard = params.idCard.originalValue
_formParams.dateOfBirth = params.dateOfBirth
_formParams.securityNumber = params.securityNumber
_formParams.noSecurityNumberDesc = params?.noSecurityNumberDesc
_formParams.homeAddress = params.homeAddress
_formParams.remark = params.remark
}
const _formRef = ref<FormExpose>(null)
const uploadFileRef = ref(null)
const _formOptions = ref<FormProMaxItemOptions<securityUnitIdListParams>>({
photo: {
type: 'custom',
label: '头像',
customRender: () => <SingleImageFileUpload height={200} v-model:value={_formParams.photo} ref={uploadFileRef} />,
},
name: {
type: 'input',
label: '姓名',
required: true,
},
idCard: {
type: 'custom',
label: '身份证',
required: true,
customRender: () => (
<Space>
<Input allow-clear v-model:value={_formParams.idCard} placeholder={'请输入身份证号'} />
<Button type={'primary'} icon={h(SearchOutlined)} onClick={() => searchSecurityUnitId()}>
</Button>
</Space>
),
},
telephone: {
type: 'input',
label: '手机号',
required: true,
},
sex: {
type: 'radioGroup',
label: '性别',
required: true,
options: [...dictSelectNodes('Sex')],
},
securityNumber: {
type: 'input',
label: '保安证号',
required: true,
},
dateOfBirth: {
type: 'datePicker',
label: '出生日期',
componentsProps: {
valueFormat: 'YYYY-MM-DD HH:mm:ss',
},
required: true,
},
workPost: {
type: 'input',
label: '工作岗位',
},
nativePlace: {
type: 'input',
label: '籍贯',
},
homeAddress: {
type: 'input',
label: '家庭住址',
},
noSecurityNumberDesc: {
type: 'input',
label: '无证说明',
},
remark: {
type: 'inputTextArea',
label: '备注',
},
})
Modal.confirm({
title: params.name ? `${params.name}】 编辑保安信息` : '新增保安人员',
width: 600,
icon: ' ',
centered: true,
content: () => <FormProMax ref={_formRef} v-model:value={_formParams} formItemOptions={_formOptions.value} />,
onOk: async () => {
await _formRef.value?.validate()
const resp = await api.post('/m2/eu/add_upd_sec_user', {
..._formParams,
})
message.success(resp.message)
clearForm()
callback && callback()
},
onCancel: async () => {
clearForm()
},
})
}
const clearForm = () => {
_formParams.snowFlakeId = ''
_formParams.serviceProjectId = ''
_formParams.securityUnitId = ''
_formParams.name = ''
_formParams.photo = ''
_formParams.telephone = ''
_formParams.workPost = ''
_formParams.sex = ''
_formParams.nativePlace = ''
_formParams.idCard = ''
_formParams.dateOfBirth = ''
_formParams.securityNumber = ''
_formParams.noSecurityNumberDesc = ''
_formParams.homeAddress = ''
_formParams.remark = ''
}
export const showEnterprisesUnit = (record_) => {
// console.log('🚀 ~ showEnterprisesUnit ~ record_:', record_)
const _tableRef = ref<ComponentExposed<typeof TableProMax>>(null)
const _columns: _TableProps['columns'] = [
{
dataIndex: 'name',
title: '姓名',
width: 100,
ellipsis: true,
},
{
dataIndex: 'idCard',
title: '身份证',
customRender: ({ text }) => {
return text.desensitizedValue
},
width: 160,
ellipsis: true,
},
{
dataIndex: 'sex',
title: '性别',
width: 60,
customRender: ({ record }) => {
return <Tag color={'success'}>{record.sex.label}</Tag>
},
},
{
dataIndex: 'dateOfBirth',
title: '出生日期',
width: 100,
ellipsis: true,
},
{
dataIndex: 'telephone',
title: '手机号',
width: 120,
ellipsis: true,
customRender: ({ text }) => text?.originalValue,
},
{
dataIndex: 'securityNumber',
title: '保安证号',
width: 150,
ellipsis: true,
},
{
dataIndex: 'nativePlace',
title: '籍贯',
width: 120,
ellipsis: true,
},
{
dataIndex: 'workPost',
title: '工作岗位',
width: 120,
ellipsis: true,
},
{
dataIndex: 'homeAddress',
title: '家庭住址',
width: 120,
ellipsis: true,
},
{
dataIndex: 'remark',
title: '备注',
width: 120,
},
{
dataIndex: 'createTime',
title: '创建时间',
width: 120,
ellipsis: true,
},
{
dataIndex: 'opt',
title: '操作',
width: 200,
fixed: 'right',
customRender: ({ record }) => (
<Space>
<Button class='btn-warn' onClick={() => saveOrUpdateEnterprisesUnit(_tableRef.value?.requestGetTableData, record, 'edit')}>
</Button>
<Button
class='btn-danger'
onClick={() =>
deleteDataModal(record.name, async () => {
const resp = await api.delete('/m2/eu/del_security_user_id', {
securityUserId: record?.snowFlakeId,
})
message.success(resp.message)
await _tableRef.value?.requestGetTableData()
})
}
>
</Button>
</Space>
),
},
]
const x: number = _columns.reduce((a, b) => a + (b.width as number), 0)
const _reqApi: _TableProps['requestApi'] = (params) => {
// console.log(record_);
;(params as PageParams<EnterprisesUnitPagerQueryParams>).params.serviceProjectId = record_.snowFlakeId
return api.post('/m2/eu/sec_user_pager', params)
}
Modal.info({
title: `${record_.name}】 管理保安人员`,
width: '80%',
centered: true,
maskClosable: true,
content: () => (
<TableProMax
scroll={{ x: x }}
ref={_tableRef}
size='small'
columns={_columns}
requestApi={_reqApi}
// searchFormOptions={{
// name: {
// type: 'input',
// label: '姓名',
// },
// securityNumber: {
// type: 'input',
// label: '保安证号',
// },
// telephone: {
// type: 'input',
// label: '手机号',
// },
// }}
v-slots={
{
tableHeader: (_) => {
return (
<Space>
<Button class='btn-success' onClick={() => saveOrUpdateEnterprisesUnit(_tableRef.value?.requestGetTableData, record_, 'add')}>
</Button>
</Space>
)
},
} as TableProMaxSlots<PoliceUnitPagerVo>
}
/>
),
})
}

View File

@ -1,72 +1,75 @@
<template>
<div>
<!-- 企事业单位 -->
<TableProMax ref="tableRef" :request-api="reqApi" :columns="columns" :searchFormOptions="searchFormOptions"
:scroll="{ x }">
<!-- <template #tableHeader>
<TableProMax
style="width: 100%"
:expandedRowRender="expandedRowRender"
:expand-column-width="50"
:defaultExpandAllRows="false"
ref="tableRef"
:request-api="reqApi"
:columns="columns"
:searchFormOptions="searchFormOptions"
:scroll="{ x: x }"
>
<template #tableHeader>
<a-space>
<a-button type="primary" @click="addUserManagement">新增用户</a-button>
<a-button type="primary" @click="saveOrUpdateEnterprisesUnit">新增企事业单位</a-button>
</a-space>
</template> -->
</template>
</TableProMax>
<a-modal v-model:open="visible" :title="title" @ok="submit" @cancel="closeModal">
<!-- <FormProMax ref="formRef" v-model:value="formParams" :form-item-options="formItemOptions" /> -->
<a-modal v-model:open="visible" :title="serviceTitle" @ok="submit" @cancel="closeModal">
<FormProMax ref="formRef" v-model:value="formParams" :form-item-options="formItemOptions" />
</a-modal>
</div>
</template>
<script setup lang="tsx">
import {storeTreeData, loadTreeFromCache} from '@/utils/DB.ts'
import { deleteDataModal } from '@/components/tsx/ModalPro.tsx'
import { EnterprisesUnitSaveOrUpdateParams } from '@/types/views/unitManage/police/policeUnit.ts'
import MapContainer from '@/components/aMap/MapContainer.vue'
import { AutoComplete, Button, Input, message, Modal, Space } from 'ant-design-vue'
import { debounce } from 'lodash-es'
import FormProMax from '@/components/form/FormProMax.vue'
import api from '@/axios'
import {ref, reactive} from 'vue'
import { ref, reactive, computed, onMounted } from 'vue'
import TableProMax from '@/components/table/TableProMax.vue'
import { TableProMaxProps } from '@/types/components/table/index.ts'
import { ComponentExposed } from 'vue-component-type-helpers'
import { dictSelectNodes } from '@/config/dict.ts'
import {publicUnitPagerQueryParams, FromItem} from '@/types/views/publicUnit.ts'
// import FormProMax from '@/components/form/FormProMax.vue'
import { publicUnitPagerQueryParams } from '@/types/views/publicUnit.ts'
import { FormProMaxItemOptions } from '@/types/components/form//index.ts'
import { FormExpose } from 'ant-design-vue/es/form/Form'
import {message} from 'ant-design-vue'
const formRef = ref<FormExpose>(null)
import { serviceProjectSaveOrUpdateParams_ } from '@/types/views/serviceManagement'
import { showEnterprisesUnit } from './index.tsx'
type _FormType = EnterprisesUnitSaveOrUpdateParams & {
contactPersonInfoName?: string
contactPersonInfoTelephone?: string
}
type TableProps = TableProMaxProps<publicUnitPagerQueryParams>
const tableRef = ref<ComponentExposed<typeof TableProMax>>(null!)
const reqApi: TableProps['requestApi'] = (params) => api.post('/management/police/enterprisesUnitPager', params) //
const reqApi: TableProps['requestApi'] = (params) => api.post('/eu/pager', params) //
const tableRef = ref<ComponentExposed<typeof TableProMax>>(null)
const columns: TableProps['columns'] = [
{
dataIndex: 'name',
title: '单位名称',
width: 200,
},
// {
// dataIndex: 'code',
// title: '',
// },
{
dataIndex: 'provinceName',
title: '行政区划',
customRender: ({ record }) => {
return `${record?.provinceName}/${record?.cityName}/${record?.districtsName}/${record?.streetName}`
},
width: 300,
},
// {
// dataIndex: 'isEnable',
// title: '',
// customRender: ({ text }) => <a-tag color={text?.extData?.color}>{text?.label}</a-tag>,
// width: 150,
// },
// {
// dataIndex: 'checkStatus',
// title: '',
// customRender: ({ record }) => {
// return record.checkStatus?.extData?.color === 'success' ? <a-tag color='green'>{record?.checkStatus?.label}</a-tag> : <a-tag color='#f50'>{record?.checkStatus?.label}</a-tag>
// },
// },
{
dataIndex: 'address',
title: '详细地址',
width: 200,
ellipsis: true,
},
{
@ -75,6 +78,7 @@ const columns: TableProps['columns'] = [
customRender: ({ record }) => {
return record?.contactPersonInfo?.name
},
width: 200,
},
{
dataIndex: 'contactPersonInfo',
@ -82,116 +86,631 @@ const columns: TableProps['columns'] = [
customRender: ({ record }) => {
return record?.contactPersonInfo?.telephone
},
width: 150,
},
{
dataIndex: 'createTime',
title: '创建时间',
width: 120,
ellipsis: true,
},
{
dataIndex: 'remark',
title: '备注',
width: 120,
ellipsis: true,
},
{
width: 200,
dataIndex: 'opt',
title: '操作',
customRender: ({ record }) => (
<Space>
<Button
class='btn-warn'
onClick={() =>
saveOrUpdateEnterprisesUnit(
{
snowFlakeId: record.snowFlakeId,
policeUnitId: record.policeUnitId,
name: record.name,
type: record.type.value,
administrativeDivisionCodes: [record.province, record.city, record.districts, record.street].filter(Boolean),
address: record.address,
point: record.point,
contactPersonInfoName: record.contactPersonInfo?.name,
contactPersonInfoTelephone: record.contactPersonInfo?.telephone,
remark: record.remark,
},
tableRef.value?.requestGetTableData
)
}
>
编辑
</Button>
<Button
class='btn-danger'
onClick={() =>
deleteDataModal(record.name, async () => {
// const resp = await api.delete('/enterprisesUnit/deleteById', {
const resp = await api.delete('/eu/del_id', {
enterprisesUnitId: record.snowFlakeId,
})
message.success(resp.message)
await tableRef.value?.requestGetTableData()
})
}
>
删除
</Button>
</Space>
),
},
]
const x: number = columns.reduce((a, b) => a + (b.width as number), 0)
const visible = ref(false)
const title = ref('新增用户')
const addUserManagement = () => {
visible.value = true
title.value = ''
}
// const x: number = columns.reduce((a, b) => a + (b.width as number), 0)
const x: number = columns.reduce((a, b) => {
// console.log('x_____________________', a, b, b.width)
return a + (b.width as number)
}, 0)
const getTree = async () => {
//
const cachedData = await loadTreeFromCache()
if (cachedData) {
console.log('未发请求')
// 使
return cachedData
const saveOrUpdateEnterprisesUnit = (params: _FormType, callback: Function) => {
const _formRef = ref<FormExpose>(null)
const _mapRef = ref<ComponentExposed<typeof MapContainer>>(null)
const _formParams = ref<_FormType>({ ...params })
let city = ''
const initMarker = (map: AMap.Map) => {
//maker point
const maker = new AMap.Marker({
position: _formParams.value.point,
draggable: true,
})
maker.on('dragend', ({ lnglat }) => {
_formParams.value.point = lnglat
})
map.clearMap()
map.add(maker)
map.setFitView()
}
const autoAddress = ref<SelectNodeVo<string>[]>([])
const _formOptions = ref<FormProMaxItemOptions<_FormType>>({
name: {
type: 'custom',
label: '单位名称',
required: true,
customRender: () => {
return (
// AutoComplete
<AutoComplete
v-model:value={_formParams.value.name}
options={autoAddress.value}
onSelect={(_, options: SelectNodeVo<string>) => {
_formParams.value.point = options.extData?.location
_formParams.value.address = options.extData?.address
initMarker(_mapRef.value?.mapInstance)
}}
onSearch={debounce((val: string) => {
//@ts-ignore
const auto = new AMap.AutoComplete({
city: city,
// input: 'tipinput',
citylimit: true,
})
auto.search(val, (status, result) => {
if (status === 'complete') {
//
autoAddress.value = result.tips?.map((e) => {
return {
value: e.name,
label: e.name,
extData: {
district: e.district,
address: e.address,
location: e.location,
},
} as SelectNodeVo<string>
})
} else {
console.log('发起了请求')
// API
const res = await api.get<any>('/common/administrativeDivisionTree')
await storeTreeData(res.data)
return res.data
autoAddress.value = []
}
}
const loadOptions = async () => {
const treeData = await getTree()
searchFormOptions.treeSelect.options = treeData
}
loadOptions()
const searchFormOptions = reactive<TableProps['searchFormOptions']>({
name: {
type: 'input',
label: '名称',
})
}, 300)}
v-slots={{
option: (item: SelectNodeVo<string>) => {
return (
<div>
<p>{item.label}</p>
<p style={{ color: '#9a9c9d' }}>
{item.extData?.district} {item.extData?.address}
</p>
</div>
)
},
treeSelect: {
type: 'cascader',
}}
>
<Input placeholder='请输入单位名称' />
</AutoComplete>
)
},
},
type: {
type: 'select',
label: '单位类型',
required: true,
// @ts-ignore
options: dictSelectNodes('EnterprisesUnitType'),
},
administrativeDivisionCodes: {
type: 'administrativeDivisionTree',
label: '行政区划',
required: true,
componentsProps: {
// @ts-ignore
displayRender: ({ labels }): string => {
city = labels[1]
return labels.join(' / ')
},
telephone: {
type: 'input',
label: '手机号',
},
// isEnable: {
// type: 'select',
// label: '',
// options: [
// {
// value: null,
// label: '',
// },
// ...dictSelectNodes('IsEnable'),
// ],
// },
},
address: {
type: 'inputTextArea',
label: '详细地址',
},
map: {
type: 'custom',
label: '经纬度',
customRender: () => (
<MapContainer
ref={_mapRef}
plugins={['AMap.AutoComplete', 'AMap.PlaceSearch']}
style={{ width: '100%', height: '300px', position: 'relative' }}
initCallback={(map) => {
const contextMenu = new AMap.ContextMenu()
contextMenu.addItem(
'标记',
() => {
const { lng, lat } = contextMenu.getPosition()
_formParams.value.point = [lng, lat]
initMarker(map)
},
0
)
map.on('rightclick', ({ lnglat }) => {
contextMenu.open(map, lnglat)
})
const formParams = ref<{
snowFlakeId?: string
name: string
sex: number
telephone: string
isEnable: any
remark?: string
}>({
name: '',
sex: 0,
telephone: '',
isEnable: 0,
})
const submit = async () => {
// await formRef.value.validate()
if (_formParams.value.point) {
initMarker(map)
}
const closeModal = () => {
visible.value = false
}
const formItemOptions = ref<FormProMaxItemOptions<FromItem>>({
name: {
}}
></MapContainer>
),
},
contactPersonInfoName: {
type: 'input',
label: '姓名',
required: true,
label: '联系人名称',
},
sex: {
type: 'radioGroup',
label: '性别',
options: dictSelectNodes('Sex'),
required: true,
},
telephone: {
contactPersonInfoTelephone: {
type: 'input',
label: '手机号',
required: true,
},
isEnable: {
type: 'radioGroup',
label: '启用状态',
options: dictSelectNodes('IsEnable'),
required: true,
label: '联系人电话',
},
remark: {
type: 'inputTextArea',
label: '备注',
},
})
Modal.confirm({
title: params.snowFlakeId ? `${params.name}】 信息编辑` : '新增企事业单位',
width: 600,
icon: ' ',
centered: true,
content: () => <FormProMax ref={_formRef} v-model:value={_formParams.value} formItemOptions={_formOptions.value} />,
onOk: debounce(async () => {
await _formRef.value?.validate()
const resp = await api.post('/eu/add_upd', {
..._formParams.value,
contactPersonInfo: {
name: _formParams.value.contactPersonInfoName,
telephone: _formParams.value.contactPersonInfoTelephone,
},
})
message.success(resp.message)
await tableRef.value?.requestGetTableData()
callback && callback()
}, 300),
})
}
const searchFormOptions = reactive<TableProps['searchFormOptions']>({
name: {
type: 'input',
label: '名称',
},
treeSelect: {
// type: 'cascader',
type: 'administrativeDivisionTree',
label: '行政区划',
},
telephone: {
type: 'input',
label: '手机号',
},
})
type _TableProps = TableProMaxProps<any>
const isRecruitSecurityHidden = ref<boolean>(false)
const visible = ref(false)
const serviceTitle = ref('新增服务项目')
const idNumberDisabled = ref<boolean>(true)
const formRef = ref<FormExpose>(null)
const enterprisesUnitId = ref('')
const netType = computed(() => {
//@ts-ignore
return formParams.value.type === 'security' ? dictSelectNodes('ServiceProjectTwoType') : dictSelectNodes('UserType' as any)
})
const formParams = ref<{
snowFlakeId?: string
enterprisesUnitId: string
securityUnitId: string
administrativeDivisionCodes?: null
projectManagerMiniProgramUserId?: string
projectManagerMiniProgramUserName?: string
name: string
type: string
twoType?: number
outsourceName?: string
isFiling?: number
idNumber?: string
serviceArea?: number
buildingTotal?: number
houseTotal?: number
staffTotal?: number
securityUserTotal?: number
remark?: string
}>({
name: '',
enterprisesUnitId: null,
type: 'security',
securityUnitId: null,
})
const securityUnitIdList = ref<any>([])
const formItemOptions = ref<FormProMaxItemOptions<serviceProjectSaveOrUpdateParams_>>({
name: {
type: 'input',
label: '服务项目名称',
required: true,
},
securityUnitId: {
type: 'select',
label: '保安单位',
required: true,
options: securityUnitIdList,
},
type: {
type: 'radioGroup',
label: '服务类型',
//@ts-ignore
options: dictSelectNodes('ServiceProjectType'),
required: true,
componentsProps: {
onChange: (e) => {
if (e.target?.value === 'security') {
isRecruitSecurityHidden.value = false
formParams.value.twoType = null
} else {
formParams.value.twoType = null
isRecruitSecurityHidden.value = true
}
},
},
},
twoType: {
required: true,
type: 'radioGroup',
label: '二级类型',
options: netType,
componentsProps: {
onChange: (e) => {
if (e.target.value !== 'outsource') {
idNumberDisabled.value = true
formParams.value.outsourceName = ''
} else {
idNumberDisabled.value = false
}
},
},
},
outsourceName: {
type: 'input',
label: '外包公司名称',
hidden: idNumberDisabled as any,
},
isFiling: {
required: true,
type: 'radioGroup',
label: '是否备案',
options: dictSelectNodes('IsOrNot'),
},
idNumber: {
type: 'input',
label: '保安服务许可证',
},
serviceArea: {
type: 'inputNumber',
label: '服务区域面积',
},
buildingTotal: {
type: 'inputNumber',
label: '楼栋数量',
componentsProps: {
formatter: (value: any) => {
return Math.round(value) ? Math.round(value) : ('' as any)
},
min: 0,
},
},
houseTotal: {
type: 'inputNumber',
label: '户数',
componentsProps: {
formatter: (value: any) => {
return Math.round(value) ? Math.round(value) : ('' as any)
},
min: 0,
},
},
staffTotal: {
type: 'inputNumber',
label: '工作人员数量',
componentsProps: {
formatter: (value: any) => {
return Math.round(value) ? Math.round(value) : ('' as any)
},
min: 0,
},
},
securityUserTotal: {
type: 'inputNumber',
label: '保安人员数量',
componentsProps: {
formatter: (value: any) => {
return Math.round(value) ? Math.round(value) : ('' as any)
},
min: 0,
},
},
remark: {
type: 'inputTextArea',
label: '备注',
},
})
const _tableRef = ref(null)
const deleteAccount = async (snowFlakeId) => {
const resp = await api.delete('/m2/eu/deleteSpById', {
serviceProjectId: snowFlakeId,
})
message.success(resp.message)
await _tableRef.value?.requestGetTableData()
}
const expandedRowRender: TableProMaxProps['expandedRowRender'] = ({ record }) => {
const _columns: _TableProps['columns'] = [
{
dataIndex: 'name',
title: '服务项目名称',
width: 120,
},
{
dataIndex: 'type',
title: '服务类型',
customRender: ({ text }) => <a-tag>{text?.label}</a-tag>,
width: 120,
},
{
dataIndex: 'twoType',
title: '二级类型',
customRender: ({ text }) => <a-tag>{text?.label}</a-tag>,
width: 120,
},
{
dataIndex: 'outsourceName',
title: '外包公司名称',
customRender: ({ record }) => {
if (record.twoType.value === 'outsource') {
return record.outsourceName
}
},
width: 120,
ellipsis: true,
},
{
dataIndex: 'isFiling',
title: '是否备案',
customRender: ({ text }) => {
if (text?.label === '是') {
return <a-tag color={'success'}>{text?.label}</a-tag>
} else {
return <a-tag color={'error'}>{text?.label}</a-tag>
}
},
width: 120,
},
{
dataIndex: 'idNumber',
title: '保安服务许可证',
width: 200,
ellipsis: true,
},
{
dataIndex: 'serviceArea',
title: '服务区域面积',
width: 60,
ellipsis: true,
},
{
dataIndex: 'buildingTotal',
title: '楼栋数量',
width: 60,
ellipsis: true,
},
{
dataIndex: 'houseTotal',
title: '户数',
width: 60,
ellipsis: true,
},
{
dataIndex: 'staffTotal',
title: '工作人员数量',
width: 60,
ellipsis: true,
},
{
dataIndex: 'securityUserTotal',
title: '保安人员数量',
width: 60,
ellipsis: true,
},
{
dataIndex: 'remark',
title: '备注',
width: 120,
},
{
dataIndex: 'createUserInfo',
title: '创建人',
width: 200,
ellipsis: true,
customRender: ({ record }) => {
return (
<div>
<p>创建人{record.createUserInfo.name} </p>
<p>创建人单位{record.createUserInfo.unitName} </p>
</div>
)
},
},
{
dataIndex: 'createTime',
title: '创建时间',
width: 120,
ellipsis: true,
},
{
dataIndex: 'opt',
title: '操作',
fixed: 'right',
width: 300,
customRender({ record }) {
return (
<a-space>
<a-button class='btn-success' onClick={() => showEnterprisesUnit(record)}>
保安人员
</a-button>
<a-button
class='btn-warn'
onClick={async () => {
visible.value = true
serviceTitle.value = '编辑服务项目'
idNumberDisabled.value = record.twoType.value !== 'outsource'
formParams.value.securityUnitId = record.securityUnitId //id
formParams.value.enterprisesUnitId = record.enterprisesUnitId //id
formParams.value.snowFlakeId = record.snowFlakeId //id
formParams.value.projectManagerMiniProgramUserId = record.projectManagerMiniProgramUserId //id
formParams.value.name = record.name
formParams.value.type = record.type.value //
formParams.value.twoType = record.twoType.value //
formParams.value.outsourceName = record.outsourceName //
formParams.value.isFiling = record.isFiling.value //
formParams.value.remark = record.remark //
formParams.value.idNumber = record.idNumber //(/
formParams.value.serviceArea = record.serviceArea //
formParams.value.buildingTotal = record.buildingTotal //
formParams.value.houseTotal = record.houseTotal //
formParams.value.staffTotal = record.staffTotal //
formParams.value.securityUserTotal = record.securityUserTotal //
}}
>
编辑
</a-button>
<a-popconfirm title='确认删除账号吗?' onConfirm={() => deleteAccount(record.snowFlakeId)}>
<a-button danger>删除</a-button>
</a-popconfirm>
</a-space>
)
},
},
]
const x2: number = _columns.reduce((a, b) => a + (b.width as number), 0)
const _reqApi: _TableProps['requestApi'] = async () => {
// @ts-ignore
return await api.get('/m2/eu/listSp', { enterprisesUnitId: record?.snowFlakeId })
}
return (
<TableProMax
scroll={{ x: x2 }}
ref={_tableRef}
size='small'
columns={_columns}
requestApi={_reqApi}
isPagination={false}
v-slots={{
tableHeader: () => {
return (
<Space>
<Button type={'primary'} onClick={() => addService(record)}>
新增服务项目
</Button>
</Space>
)
},
}}
/>
)
}
const closeModal = async () => {
visible.value = false
formParams.value = {
securityUnitId: '',
enterprisesUnitId: '',
administrativeDivisionCodes: '',
name: '',
type: 'security',
idNumber: '',
serviceArea: null,
buildingTotal: null,
houseTotal: null,
staffTotal: null,
securityUserTotal: null,
remark: '',
}
formRef.value.resetFields()
enterprisesUnitId.value = ''
serviceTitle.value = '新增服务项目'
idNumberDisabled.value = false
}
const submit = async () => {
await formRef.value.validate()
const serviceProjectSaveOrUpdateParams = { ...formParams.value }
const resp = await api.post('/m2/eu/add_upd_sp', serviceProjectSaveOrUpdateParams)
message.success(resp.message)
await _tableRef.value.requestGetTableData()
await closeModal()
}
onMounted(async () => {
const res = await api.get('/management/listSecurityUnit')
securityUnitIdList.value = res.data
})
const addService = function (record) {
formParams.value.enterprisesUnitId = record.snowFlakeId //Id
visible.value = true
}
</script>

View File

@ -15,6 +15,8 @@
<div style="height: 100%">
<a-form-item v-if="options.length > 0" label="行政区划">
<a-cascader
change-on-select
:load-data="loadData"
:field-names="{ label: 'label', value: 'value', children: 'children' }"
@change="cascaderChange"
style="width: 300px"
@ -77,7 +79,7 @@
</template>
<script setup lang="ts">
import { storeTreeData, loadTreeFromCache } from '@/utils/DB.ts'
// import { storeTreeData, loadTreeFromCache } from '@/utils/DB.ts'
import { useRouter } from 'vue-router'
const router = useRouter()
import { ExclamationCircleOutlined } from '@ant-design/icons-vue'
@ -89,35 +91,53 @@ defineComponent({
name: 'Register',
})
onMounted(() => {
getTree()
getTree(0)
})
import type { CascaderProps } from 'ant-design-vue'
const loadData: CascaderProps['loadData'] = async (selectedOptions) => {
const targetOption = selectedOptions[selectedOptions.length - 1]
targetOption.loading = true
const res = await api.get<any>('/common/administrativeDivisionByParentCode', { parentCode: targetOption.value })
for (let index = 0; index < res.data.length; index++) {
delete res.data[index].children
}
targetOption.children = [...res.data]
options.value = [...options.value]
targetOption.loading = false
}
const labelCol = { style: { width: '120px' } }
const wrapperCol = { span: 14 }
const filter: ShowSearchType['filter'] = (inputValue, path) => {
return path.some((option) => option.title.toLowerCase().indexOf(inputValue.toLowerCase()) > -1)
}
const value = ref<string[]>([])
const options = ref<any[]>([])
const options = ref<CascaderProps['options']>([])
const cascaderChange = (value: any): void => {
formState.administrativeDivisionCodes = [...value]
}
// IndexedDB
const getTree = async () => {
//
const cachedData = await loadTreeFromCache()
if (cachedData) {
console.log('未发请求')
// 使
options.value = cachedData
// console.log('Loaded from cache:', cachedData)
} else {
console.log('发起了请求')
// API
const res = await api.get<any>('/common/administrativeDivisionTree')
options.value = res.data
await storeTreeData(res.data)
const getTree = async (parentCode: string | number) => {
const res = await api.get<any>('/common/administrativeDivisionByParentCode', { parentCode })
for (let index = 0; index < res.data.length; index++) {
delete res.data[index].children
}
options.value = res.data
// //
// const cachedData = await loadTreeFromCache()
// if (cachedData) {
// console.log('')
// // 使
// options.value = cachedData
// // console.log('Loaded from cache:', cachedData)
// } else {
// console.log('')
// // API
// const res = await api.get<any>('/common/administrativeDivisionTree', { level: 0 })
// options.value = res.data
// await storeTreeData(res.data)
// }
}
import type { Rule } from 'ant-design-vue/es/form'
import type { FormInstance } from 'ant-design-vue'
@ -245,6 +265,7 @@ const getCheckStatus = async () => {
})
}
import { useUserStore } from '@/stores/modules/userStore.ts'
import { log } from 'console'
const showConfirm = (columnsDate: dataStatus) => {
if (columnsDate.checkStatus.value === 0) {
Modal.success({

View File

@ -2,13 +2,7 @@
<!-- 后台用户 -->
<!-- template 内部必须有一个根节点 div否则<Transition>将会失效所以这个<a-modal></a-modal> div -->
<div>
<TableProMax
ref="tableRef"
:request-api="reqApi"
:columns="columns"
:searchFormOptions="searchFormOptions"
:scroll="{x}"
>
<TableProMax ref="tableRef" :request-api="reqApi" :columns="columns" :searchFormOptions="searchFormOptions" :scroll="{ x }">
<template #tableHeader>
<a-space>
<a-button type="primary" @click="addUserManagement">新增用户</a-button>
@ -23,30 +17,31 @@
</template>
<script setup lang="tsx">
import TableProMax from "@/components/table/TableProMax.vue";
import {TableProMaxProps} from "@/types/components/table";
import TableProMax from '@/components/table/TableProMax.vue'
import { TableProMaxProps } from '@/types/components/table'
import { message } from 'ant-design-vue'
import { dictSelectNodes } from '@/config/dict.ts'
// const enumsSex = ref<any[]>(dictSelectNodes('Sex'))
// const enumsIsEnable = ref<any[]>(dictSelectNodes('IsEnable'))
import api from '@/axios/index.ts'
import { ref, reactive, onMounted } from 'vue'
import FormProMax from "@/components/form/FormProMax.vue";
import {FormProMaxItemOptions} from "@/types/components/form";
import {FormExpose} from "ant-design-vue/es/form/Form";
import {publicUnitPagerQueryParams, FromItem} from "@/types/views/publicUnit.ts";
import FormProMax from '@/components/form/FormProMax.vue'
import { FormProMaxItemOptions } from '@/types/components/form'
import { FormExpose } from 'ant-design-vue/es/form/Form'
import { publicUnitPagerQueryParams, FromItem } from '@/types/views/publicUnit.ts'
import { ComponentExposed } from 'vue-component-type-helpers'
const tableRef = ref<ComponentExposed<typeof TableProMax>>(null!)
const formRef = ref<FormExpose>(null)
type TableProps = TableProMaxProps<publicUnitPagerQueryParams>
const reqApi: TableProps['requestApi'] = (params) => api.post('/management/police/user/pager', params) //
const formParams = ref<{
snowFlakeId?: string,
name: string,
sex: number,
telephone: string,
isEnable: any,
// const reqApi: TableProps['requestApi'] = (params) => api.post('/management/police/user/pager', params) //
const reqApi: TableProps['requestApi'] = (params) => api.post('/m2/user/pager', params) //
const formParams = ref<{
snowFlakeId?: string
name: string
sex: number
telephone: string
isEnable: any
}>({
name: '',
sex: 0,
@ -58,9 +53,7 @@ const formItemOptions = ref<FormProMaxItemOptions<FromItem>>({
type: 'input',
label: '姓名',
required: true,
rules: [
{required: true, message: '请输入姓名'}
],
rules: [{ required: true, message: '请输入姓名' }],
},
sex: {
type: 'radioGroup',
@ -78,11 +71,11 @@ const formItemOptions = ref<FormProMaxItemOptions<FromItem>>({
validator: (rule, value) => {
const phoneRegex = /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/
if (!value) {
return Promise.reject('手机号不能为空');
return Promise.reject('手机号不能为空')
} else if (!phoneRegex.test(value)) {
return Promise.reject('手机号格式不正确');
return Promise.reject('手机号格式不正确')
} else {
return Promise.resolve();
return Promise.resolve()
}
},
trigger: 'blur',
@ -101,25 +94,25 @@ const columns: TableProps['columns'] = [
dataIndex: 'account',
title: '账号',
width: 100,
ellipsis: true
ellipsis: true,
},
{
dataIndex: 'name',
title: '名称',
width: 200,
ellipsis: true
ellipsis: true,
},
{
dataIndex: 'sex',
title: '性别',
customRender: ({ text }) => <a-tag>{text?.label}</a-tag>,
width: 150
width: 150,
},
{
dataIndex: 'telephone',
title: '手机号码',
width: 150,
ellipsis: true
ellipsis: true,
},
{
dataIndex: 'createTime',
@ -132,65 +125,71 @@ const columns: TableProps['columns'] = [
dataIndex: 'isEnable',
title: '是否启用',
customRender: ({ text }) => <a-tag color={text?.extData?.color}>{text?.label}</a-tag>,
width: 150
width: 150,
},
{
dataIndex: 'opt',
title: '操作',
fixed: "right",
fixed: 'right',
customRender({ record }) {
return (
record.isAdmin.value === 1 ?
return record.isAdmin.value === 1 ? (
<a-space>
<a-popconfirm
style="width:100%"
title="确认删除账号吗?"
style='width:100%'
title='确认删除账号吗?'
onConfirm={async () => {
const resp = await api.delete('/management/police/user/deleteById', {
{
/* const resp = await api.delete('/management/police/user/deleteById', { */
}
const resp = await api.delete('/m2/user/del_id', {
managementPoliceUnitUserId: record.snowFlakeId,
})
message.success(resp.message)
await tableRef.value?.requestGetTableData()
}}>
<a-button type="primary" danger>删除</a-button>
}}
>
<a-button type='primary' danger>
删除
</a-button>
</a-popconfirm>
<a-button type="primary" onClick={async () => {
<a-button
type='primary'
onClick={async () => {
visible.value = true
title.value = "编辑用户"
title.value = '编辑用户'
formParams.value.snowFlakeId = record.snowFlakeId
formParams.value.name = record.name,
formParams.value.sex = record.sex.value,
formParams.value.telephone = record.telephone,
formParams.value.isEnable = record.isEnable?.value
}}>
;(formParams.value.name = record.name), (formParams.value.sex = record.sex.value), (formParams.value.telephone = record.telephone), (formParams.value.isEnable = record.isEnable?.value)
}}
>
编辑
</a-button>
</a-space>
:
) : (
<div>超级管理员不能编辑</div>
)
}
},
},
]
const x: number = columns.reduce((a, b) => a + (b.width as number), 0)
const searchFormOptions: TableProps["searchFormOptions"] = {
const searchFormOptions: TableProps['searchFormOptions'] = {
name: {
type: 'input',
label: '名称'
}, sex: {
label: '名称',
},
sex: {
type: 'select',
label: '性别',
options: [
{
value: null,
label: '全部'
}, ...dictSelectNodes('Sex')
]
label: '全部',
},
...dictSelectNodes('Sex'),
],
},
telephone: {
type: 'input',
label: '手机号'
label: '手机号',
},
isEnable: {
type: 'select',
@ -198,10 +197,11 @@ const searchFormOptions: TableProps["searchFormOptions"] = {
options: [
{
value: null,
label: '全部'
}, ...dictSelectNodes('IsEnable')
]
}
label: '全部',
},
...dictSelectNodes('IsEnable'),
],
},
}
const visible = ref(false)
const title = ref('新增用户')
@ -222,13 +222,13 @@ const submit = async () => {
sex: formParams.value.sex,
telephone: formParams.value.telephone,
isEnable: formParams.value.isEnable,
}
const resp = await api.post('/management/police/user/saveOrUpdate', managementSecurityUnitUserSaveOrUpdateParams)
// const resp = await api.post('/management/police/user/saveOrUpdate', managementSecurityUnitUserSaveOrUpdateParams)
const resp = await api.post('/m2/user/add_upd', managementSecurityUnitUserSaveOrUpdateParams)
message.success(resp.message)
tableRef.value?.requestGetTableData()
closeModal()
}
const closeModal = () => {
@ -236,7 +236,7 @@ const closeModal = () => {
name: '',
sex: 0,
telephone: '',
isEnable: 0
isEnable: 0,
}
visible.value = false
title.value = '新增用户'

View File

@ -1,8 +1,198 @@
<template>
<div>
<!-- 三色预警 -->
三色预警
<TableProMax ref="tableRef" :request-api="reqApi" :columns="columns">
</TableProMax>
<div>
<a-modal v-model:open="open" :title="title" @ok="open = false" width="80%">
<a-table :columns="TableColumns" :data-source="TableData" bordered >
<template #bodyCell="{ column, text }">
<template v-if="column.dataIndex === 'name'">
<div>{{text}}{{column}}</div>
</template>
</template>
</a-table>
</a-modal>
</div>
</div>
</template>
<script setup lang="ts"></script>
<script setup lang="tsx">
import TableProMax from "@/components/table/TableProMax.vue";
import api from "@/axios";
import {TableProMaxProps} from "@/types/components/table";
import {
AssessmentRecordPagerQueryParams,
AssessmentRecordPagerVo, DeductedDetailRes,
} from "@/types/views/assessmentRecord.ts";
import {ComponentExposed} from "vue-component-type-helpers";
import { ref} from "vue";
import {Modal, TableColumnType} from "ant-design-vue";
const tableRef = ref<ComponentExposed<typeof TableProMax>>(null!)
type TableProps = TableProMaxProps<AssessmentRecordPagerVo,AssessmentRecordPagerQueryParams>
const open = ref<boolean>(false);
const title = ref('扣分项')
const reqApi: TableProps['requestApi'] = (params) => api.post('/assessmentRecord/pager', params) //
const columns: TableProps['columns'] = [
{
dataIndex: 'enterprisesUnitName',
title: '单位名称'
}, {
dataIndex: 'type',
title: '类型',
customRender: ({text}) => text?.label
}, {
dataIndex: 'ckProjectName',
title: '考核项目'
}, {
dataIndex: 'totalScore',
title: '总分'
}, {
dataIndex: 'deductionPointsTotal',
title: '扣分',
customRender:({record})=>{
if (!record.deductionPointsTotal) {
return <a-tag color="green">0</a-tag>
}
return <a-tag class="pointer" color="red" onClick={()=>deductedDetail(record)}>{record.deductionPointsTotal}</a-tag>
}
}, {
dataIndex: 'result',
title: '得分',
customRender: ({record}) => record.totalScore - record.deductionPointsTotal
}, {
dataIndex: 'policeUnitName',
title: '考核单位'
}, {
dataIndex: 'createUserName',
title: '考核人'
}, {
dataIndex: 'createTime',
title: '考核时间'
}, {
dataIndex: 'remark',
title: '考核备注'
}, {
dataIndex: 'signature',
title: '签字',
customRender:({record})=>{
return <a-button onClick={()=>{
Modal.info({
title: `${record.enterprisesUnitName}${record.ckProjectName} 签字结果`,
content: () => <>
<div>审核人签字: <a-image src={record.assessmentUserSignature}/>
</div>
<div>被审核单位人员签字: <a-image src={record.byAssessmentEnterprisesUnitUserSignature}/></div>
</>
})
}}>查看</a-button>
},
}
]
const TableData = [
{
key: '1',
name: 'John Brown',
age: 32,
tel: '0571-22098909',
phone: 18889898989,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
tel: '0571-22098333',
phone: 18889898888,
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
tel: '0575-22098909',
phone: 18900010002,
address: 'Sidney No. 1 Lake Park',
},
{
key: '4',
name: 'Jim Red',
age: 18,
tel: '0575-22098909',
phone: 18900010002,
address: 'London No. 2 Lake Park',
},
{
key: '5',
name: 'Jake White',
age: 18,
tel: '0575-22098909',
phone: 18900010002,
address: 'Dublin No. 2 Lake Park',
},
];
const sharedOnCell = (_, index) => {
if (index === 4) {
return { colSpan: 0 };
}
};
const TableColumns: TableColumnType[] = [
{
title: 'Name',
dataIndex: 'name',
customCell: (_, index) => ({
colSpan: index < 4 ? 1 : 5,
}),
},
{
title: 'Age',
dataIndex: 'age',
customCell: sharedOnCell,
},
{
title: 'Home phone',
colSpan: 2,
dataIndex: 'tel',
customCell: (_, index) => {
if (index === 2) {
return { rowSpan: 2 };
}
// These two are merged into above cell
if (index === 3) {
return { rowSpan: 0 };
}
if (index === 4) {
return { colSpan: 0 };
}
},
},
{
title: 'Phone',
colSpan: 0,
dataIndex: 'phone',
customCell: sharedOnCell,
},
{
title: 'Address',
dataIndex: 'address',
customCell: sharedOnCell,
},
];
const deductedDetail = async (res:any)=>{
const {data} = await api.get<DeductedDetailRes[]>('/assessmentRecord/deductedDetail', {assessmentRecordId: res.snowFlakeId})
open.value = true
console.log(data)
}
</script>
<style scoped lang="scss">
</style>

View File

@ -1,20 +1,37 @@
// /// <reference types="vite/client" />
// // vue3导入模块报红解决方案——找不到模块“./XXX.vue”或其相应的类型声明
// // 报错原因是typescript 只能理解 .ts 文件,无法理解 .vue文件
// // 因此需要给.vue文件加上类型说明文件
// declare module '*.vue' {
// import type { DefineComponent } from 'vue'
// // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
// const component: DefineComponent<{}, {}, any>
// export default component
// }
// declare module 'lodash-es' {
// import { includes, isEmpty, ceil, divide } from 'lodash';
// export { includes, isEmpty, ceil, divide };
// }
// interface ImportMetaEnv {
// // 项目名称
// readonly VITE_APP_NAME: string;
// // 当前环境
// readonly VITE_APP_ENV: 'development' | 'production';
// // 启动端口
// readonly VITE_APP_PORT: number;
// // axios
// readonly VITE_APP_BASE_API: string;
// readonly VITE_APP_PROXY_URL: string;
// // RSA公钥
// readonly VITE_APP_RSA_PUBLIC_KEY: string;
// }
/// <reference types="vite/client" />
// vue3导入模块报红解决方案——找不到模块“./XXX.vue”或其相应的类型声明
// 报错原因是typescript 只能理解 .ts 文件,无法理解 .vue文件
// 因此需要给.vue文件加上类型说明文件
declare module '*.vue' {
import type { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>
export default component
}
declare module 'lodash-es' {
import { includes, isEmpty, ceil, divide } from 'lodash';
export { includes, isEmpty, ceil, divide };
}
interface ImportMetaEnv {
// 项目名称
readonly VITE_APP_NAME: string;
@ -22,14 +39,30 @@ interface ImportMetaEnv {
readonly VITE_APP_ENV: 'development' | 'production';
// 启动端口
readonly VITE_APP_PORT: number;
// 模块名称
readonly VITE_APP_MODULE_NAME: string;
// axios
readonly VITE_APP_BASE_API: string;
readonly VITE_APP_PROXY_URL: string;
// minio
readonly VITE_APP_MINIO_URL: string
readonly VITE_APP_MINIO_BUCKET: string
// RSA公钥
readonly VITE_APP_RSA_PUBLIC_KEY: string;
// 高德
VITE_APP_GAODE_KEY: string
VITE_APP_GAODE_VERSION: string
VITE_APP_SECURITY_JS_CODE: string
}
declare module '*.vue' {
import { DefineComponent, readonly } from "vue"
const component: DefineComponent<{}, {}, any>
export default component
}

View File

@ -1,82 +1,88 @@
{
"compilerOptions": {
"composite": true, // [ty-reference](1)
"target": "ES2020", // JavaScript
"useDefineForClassFields": true, // 使 `defineProperty`
"module": "ESNext", //
"lib": [ //
"ES2020", // ES2020
"DOM", // DOM
"DOM.Iterable" // DOM
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": [
"ES2020",
"DOM",
"DOM.Iterable"
],
"skipLibCheck": true, //
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler", // bundler
"allowImportingTsExtensions": true, // TypeScript
"isolatedModules": true, //
"moduleDetection": "force", // ESM
"noEmit": false, //
"declaration": true, // .d.ts
"emitDeclarationOnly": true, // JS
"jsx": "preserve", // JSX
"jsxImportSource": "vue", // JSX Vue
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "preserve",
"jsxImportSource": "vue",
/* Linting */
"strict": false, //
"noUnusedLocals": true, // 使
"noUnusedParameters": true, // 使
"noFallthroughCasesInSwitch": true, // switch fallthrough
"baseUrl": "./", //
"strict": false,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": "./",
/* */
"paths": { //
"@/*": [ // @/ src/
"./src/*"
"paths": {
"@/*": [
"src/*"
]
},
"allowSyntheticDefaultImports": true //
"allowSyntheticDefaultImports": true
},
"include": [ //
"src/**/*.ts", // src TypeScript
"src/**/*.d.ts", // src
"src/**/*.tsx", // src JSX
"src/**/*.vue", // src Vue
"global.d.ts"
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue"
]
}
// //
// {
// "compilerOptions": {
// "target": "ES2020",
// "useDefineForClassFields": true,
// "module": "ESNext",
// "lib": [
// "ES2020",
// "DOM",
// "DOM.Iterable"
// "composite": true, // [ty-reference](1)
// "target": "ES2020", // JavaScript
// "useDefineForClassFields": true, // 使 `defineProperty`
// "module": "ESNext", //
// "lib": [ //
// "ES2020", // ES2020
// "DOM", // DOM
// "DOM.Iterable" // DOM
// ],
// "skipLibCheck": true,
// "skipLibCheck": true, //
// /* Bundler mode */
// "moduleResolution": "bundler",
// "allowImportingTsExtensions": true,
// "isolatedModules": true,
// "moduleDetection": "force",
// "noEmit": true,
// "jsx": "preserve",
// "moduleResolution": "bundler", // bundler
// "allowImportingTsExtensions": true, // TypeScript
// "isolatedModules": true, //
// "moduleDetection": "force", // ESM
// "noEmit": false, //
// "declaration": true, // .d.ts
// "emitDeclarationOnly": true, // JS
// "jsx": "preserve", // JSX
// "jsxImportSource": "vue", // JSX Vue
// /* Linting */
// "strict": true,
// "noUnusedLocals": true,
// "noUnusedParameters": true,
// "noFallthroughCasesInSwitch": true,
// "baseUrl": "/",
// "strict": false, //
// "noUnusedLocals": true, // 使
// "noUnusedParameters": true, // 使
// "noFallthroughCasesInSwitch": true, // switch fallthrough
// "baseUrl": "./", //
// /* */
// "paths": {
// "@/*": [
// "paths": { //
// "@/*": [ // @/ src/
// "./src/*"
// ]
// },
// "allowSyntheticDefaultImports": true //
// },
// "include": [
// "src/**/*.ts",
// "src/**/*.tsx",
// "src/**/*.vue"
// "include": [ //
// "src/**/*.ts", // src TypeScript
// "src/**/*.d.ts", // src
// "src/**/*.tsx", // src JSX
// "src/**/*.vue", // src Vue
// "global.d.ts"
// ]
// }

View File

@ -1,40 +1,43 @@
{
"compilerOptions": {
"baseUrl": "/", //
"paths": { //
"@/*": [ // @/ src/
"./src/*"
]
}
},
"files": [], //
"references": [ // tsconfig
{
"path": "./tsconfig.app.json" // tsconfig.app.json
},
{
"path": "./tsconfig.node.json" // tsconfig.node.json
}
],
"include": [ //
"env.d.ts", //
"src/vite-env.d.ts", // Vite
"src/**/*.ts", // src TypeScript
"src/**/*.tsx", // src JSX
"src/**/*.vue", // src Vue
"global.d.ts", // global.d.ts
"vite.config.ts" // Node
]
}
// //
// {
// "files": [],
// "references": [
// {
// "path": "./tsconfig.app.json"
// },
// {
// "path": "./tsconfig.node.json"
// }
// "compilerOptions": {
// "jsx": "react",
// "baseUrl": "/", //
// "paths": { //
// "@/*": [ // @/ src/
// "./src/*"
// ]
// }
// },
// "files": [], //
// "references": [ // tsconfig
// {
// "path": "./tsconfig.app.json" // tsconfig.app.json
// },
// {
// "path": "./tsconfig.node.json" // tsconfig.node.json
// }
// ],
// "include": [ //
// "env.d.ts", //
// "src/vite-env.d.ts", // Vite
// "src/**/*.ts", // src TypeScript
// "src/**/*.tsx", // src JSX
// "src/**/*.vue", // src Vue
// "global.d.ts", // global.d.ts
// "vite.config.ts" // Node
// ]
// }
//
{
"files": [],
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.node.json"
}
]
}

View File

@ -1,47 +1,47 @@
{
"compilerOptions": {
"composite": true, // [ty-reference](1)
"target": "ES2022", // JavaScript
"lib": [ //
"ES2023" // ES2023
],
"module": "ESNext", //
"skipLibCheck": true, //
/* Bundler mode */
"moduleResolution": "bundler", // bundler
"allowImportingTsExtensions": true, // TypeScript
"isolatedModules": true, //
"moduleDetection": "force", // ESM
"noEmit": false, //
"declaration": true, // .d.ts
"emitDeclarationOnly": true, // JS
"strict": true, //
"noUnusedLocals": true, // 使
"noUnusedParameters": true, // 使
"noFallthroughCasesInSwitch": true // switch fallthrough
},
"include": [ //
"vite.config.ts" // Vite
]
}
//
// {
// "compilerOptions": {
// "target": "ES2022",
// "lib": ["ES2023"],
// "module": "ESNext",
// "skipLibCheck": true,
// "composite": true, // [ty-reference](1)
// "target": "ES2022", // JavaScript
// "lib": [ //
// "ES2023" // ES2023
// ],
// "module": "ESNext", //
// "skipLibCheck": true, //
// /* Bundler mode */
// "moduleResolution": "bundler",
// "allowImportingTsExtensions": true,
// "isolatedModules": true,
// "moduleDetection": "force",
// "noEmit": true,
// /* Linting */
// "strict": true,
// "noUnusedLocals": true,
// "noUnusedParameters": true,
// "noFallthroughCasesInSwitch": true
// "moduleResolution": "bundler", // bundler
// "allowImportingTsExtensions": true, // TypeScript
// "isolatedModules": true, //
// "moduleDetection": "force", // ESM
// "noEmit": false, //
// "declaration": true, // .d.ts
// "emitDeclarationOnly": true, // JS
// "strict": true, //
// "noUnusedLocals": true, // 使
// "noUnusedParameters": true, // 使
// "noFallthroughCasesInSwitch": true // switch fallthrough
// },
// "include": ["vite.config.ts"]
// "include": [ //
// "vite.config.ts" // Vite
// ]
// }
//
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["vite.config.ts"]
}

View File

@ -14,7 +14,11 @@ export default defineConfig(({ mode }) => {
define: {
__APP_ENV: JSON.stringify(env)
},
base: '/',
// base: './',
base: '/policeManagement/',
// base: '/',
plugins: [
vue(),
vueJsx(),
@ -45,7 +49,7 @@ export default defineConfig(({ mode }) => {
}
},
build: {
outDir: 'dist',
outDir: 'policeManagement',
target: 'modules',
chunkSizeWarningLimit: 1500,
minify: 'terser',

View File

@ -3,3 +3,4 @@ target
logs
temp
HELP.md
rebel.xml

56
policeSecurityServer/Jenkinsfile vendored Normal file
View File

@ -0,0 +1,56 @@
pipeline {
agent any
tools {
jdk "jdk-17.0.11"
maven "apache-maven-3.8.8"
}
stages {
stage('拉取代码') {
steps {
echo '开始拉取代码'
checkout scmGit(branches: [[name: '*/main']], extensions: [], userRemoteConfigs: [[credentialsId: '3', url: 'http://175.6.124.250:3100/luozhun/policeSecurity.git']])
echo '代码拉取成功'
}
}
stage('构建后台服务') {
steps{
echo '开始构建后台服务'
sh 'cd /var/jenkins_home/workspace/警保联动后端服务/policeSecurityServer && mvn clean -DskipTests=true package -P prod'
echo '后台服务构建完成'
}
}
stage('ssh远程推送代码'){
steps{
echo '开始推送构建产物'
script{
def remote = [:]
remote.name = 'server-118.253.177.137'
remote.host = '118.253.177.137'
remote.port = 55555
remote.allowAnyHosts = true
withCredentials([usernamePassword(credentialsId: '4', passwordVariable: 'password', usernameVariable: 'username')]) {
remote.user = "${username}"
remote.password = "${password}"
}
sshCommand remote: remote, command: 'pwd=$(pwd) echo "ssh连接成功当前工作目录$(pwd)"'
sshCommand remote: remote, command: 'echo "停止后台服务..."'
sshCommand remote: remote, command: '''docker stop policeSecurityServer'''
sshCommand remote: remote, command: 'echo "后台服务已停止..."'
sshCommand remote: remote, command: 'echo "删除原来的server.jar..."'
sshRemove remote: remote, path: '/home/javaProject/policeSecurity/policeSecurityServer.jar'
sshCommand remote: remote, command: 'echo "server.jar删除成功!"'
sshCommand remote: remote, command: 'echo "将构建的server.jar发送到服务器..."'
sshPut remote: remote, from: '/var/jenkins_home/workspace/警保联动后端服务/policeSecurityServer/target/policeSecurityServer.jar', into: '/home/javaProject/policeSecurity'
sshCommand remote: remote, command: 'echo "server.jar发送成功!"'
sshCommand remote: remote, command: 'echo "启动后台服务..."'
sshCommand remote: remote, command: '''docker start policeSecurityServer'''
sshCommand remote: remote, command: 'echo "后台服务启动成功!"'
}
}
}
}
}

View File

@ -23,6 +23,7 @@
<easyexcel.version>3.3.4</easyexcel.version>
<mysql.driver.version>8.0.32</mysql.driver.version>
<mybatis.plus.version>3.5.7</mybatis.plus.version>
<geotools.version>25.2</geotools.version>
<druid.version>1.2.20</druid.version>
<minio.version>8.4.3</minio.version>
<okhttp.version>4.8.1</okhttp.version>
@ -190,6 +191,17 @@
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis.plus.version}</version>
</dependency>
<!-- 处理地理空间数据的读取、写入、转换、分析以及可视化 geotools-->
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-main</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-geojson</artifactId>
<version>${geotools.version}</version>
</dependency>
<!-- minio对象存储 https://www.minio.org.cn/ -->
<dependency>
<groupId>io.minio</groupId>

Some files were not shown because too many files have changed in this diff Show More