ZaiZai 1 tahun lalu
induk
melakukan
82d77bd7f6

+ 1 - 1
public/version.json

@@ -1,3 +1,3 @@
 {
-  "value": "20240920165200"
+  "value": "20240924175332"
 }

+ 10 - 18
src/App.vue

@@ -29,30 +29,22 @@ watch(() => [
 
 onMounted(() => {
     // 监听设备连接
-    navigator.usb.addEventListener('connect', (event) => {
-        console.log('USB 设备已连接:', event.device)
+    navigator.usb.addEventListener('connect', ({ device }) => {
+        console.log('USB 设备已连接:', device)
+        if (device.manufacturerName === 'CFIST') {
+            appStore.setIsUKey(true)
+        }
     })
 
     // 监听设备断开
-    navigator.usb.addEventListener('disconnect', (event) => {
-        console.log('USB 设备已断开:', event.device)
+    navigator.usb.addEventListener('disconnect', ({ device }) => {
+        console.log('USB 设备已断开:', device)
+        if (device.manufacturerName === 'CFIST') {
+            appStore.setIsUKey(false)
+        }
     })
 })
 
-const testClick1 = () => {
-    navigator.usb.requestDevice({ filters: [{ vendorId: 10367 }] }).then(device => {
-        console.log(device)
-    }).catch(error => {
-        console.log('未选择')
-    })
-}
-
-const testClick2 = () => {
-    navigator.usb.getDevices().then(devices => {
-        console.log(devices)
-    })
-}
-
 nextTick(() => {
     window.$localModel = website.localModel
     setUserTheme(appStore.getThemeVal, appStore.getColor)

TEMPAT SAMPAH
src/assets/view/ukey.png


+ 105 - 36
src/global/components/hc-sms-auth/index.vue

@@ -1,46 +1,62 @@
 <template>
-    <hc-new-dialog v-model="showModal" title="短信认证" widths="26rem" @close="cancelClick">
-        <el-form ref="reportFormRef" :model="reportModel" :rules="reportRules" label-width="auto" size="large">
-            <el-form-item label="手机号码">
-                <el-input v-model="phoneVal" disabled placeholder="手机号码" />
-            </el-form-item>
-            <el-form-item class="hc-input-button-group" label="验证码" prop="code">
-                <el-input v-model="reportModel.code" placeholder="请输入验证码" />
-                <el-button :disabled="isDisabled" size="large" type="primary" @click="getCodeClick">
-                    {{ isDisabled ? `倒计时${currentTime}s` : '获取验证码' }}
-                </el-button>
-            </el-form-item>
-        </el-form>
-        <template #footer>
-            <div class="dialog-footer">
-                <el-button size="large" @click="cancelClick">
-                    <HcIcon name="close" />
+    <hc-dialog v-model="showModal" ui="hc-sms-auth-dialog" widths="30rem" :padding="false" :footer="false" :is-close="false" @close="cancelClick">
+        <hc-tab-card :tabs="tabsData" :tab-key="tabsKey" is-action-btn contents @change="tabsChange">
+            <div v-if="tabsKey === '1'" class="relative p-[14px]">
+                <el-form ref="reportFormRef" :model="reportModel" :rules="reportRules" label-width="auto">
+                    <el-form-item label="手机号码">
+                        <el-input v-model="phoneVal" disabled placeholder="手机号码" />
+                    </el-form-item>
+                    <el-form-item class="hc-input-button-group" label="验证码" prop="code">
+                        <el-input v-model="reportModel.code" placeholder="请输入验证码" />
+                        <el-button :disabled="isDisabled" type="primary" @click="getCodeClick">
+                            {{ isDisabled ? `倒计时${currentTime}s` : '获取验证码' }}
+                        </el-button>
+                    </el-form-item>
+                </el-form>
+            </div>
+            <div v-if="tabsKey === '2'" class="relative p-[14px]">
+                <div class="relative mb-24px text-center">
+                    <el-button v-if="isUKey" type="warning" size="large" @click="linkUkeyClick">已连接UKey</el-button>
+                    <el-button v-else type="success" size="large" @click="linkUkeyClick">点击连接UKey</el-button>
+                </div>
+                <div class="mb-10px text-red-6">操作步骤:</div>
+                <div class="mb-10px">第一步、插入UKey在电脑上(一定要先插入密钥UKey),点击上方按钮连接配对</div>
+                <div class="mb-10px">第二步、选择【Utap】,点击【连接】,当页面显示【已配对】</div>
+                <div class="relative">
+                    <img :src="HcImgUKey" class="w-full" alt="">
+                </div>
+            </div>
+            <template #action>
+                <el-button @click="cancelClick">
+                    <hc-icon name="close" />
                     <span>取消</span>
                 </el-button>
-                <el-button :loading="isLoading" hc-btn type="primary" @click="confirmClick">
-                    <HcIcon name="check" />
+                <el-button :loading="isLoading" type="primary" @click="confirmClick">
+                    <hc-icon name="check" />
                     <span>确认</span>
                 </el-button>
-            </div>
-        </template>
-    </hc-new-dialog>
+            </template>
+        </hc-tab-card>
+    </hc-dialog>
 </template>
 
 <script setup>
 import { ref, watch } from 'vue'
 import { useAppStore } from '~src/store'
+import { formValidate } from 'js-fast-way'
 import { saveSmsTimeout, sendNotice } from '~api/other'
 import config from '~src/config/index'
-import { formValidate } from 'js-fast-way'
+import HcImgUKey from '~src/assets/view/ukey.png'
+
 //参数
 const props = defineProps({
     show: {
         type: Boolean,
         default: false,
     },
-    loading: {
-        type: Boolean,
-        default: false,
+    tabKey: {
+        type: String,
+        default: '1',
     },
 })
 
@@ -51,19 +67,36 @@ const userStore = useAppStore()
 const userInfo = ref(userStore.getUserInfo)
 const phoneVal = ref(config.smsPhone + '' || userInfo.value.phone + '')
 const showModal = ref(props.show)
-const isLoading = ref(props.loading)
+
+
+//Ukey是否存在
+const isUKey = ref(userStore.getIsUKey)
+watch(() => userStore.getIsUKey, (ukey) => {
+    isUKey.value = ukey
+}, { immediate: true, deep: true })
 
 //监听
-watch(() => [
-    props.show,
-    props.loading,
-    userStore.getUserInfo,
-], ([show, loading, user]) => {
+watch(() => [props.show, userStore.getUserInfo], ([show, user]) => {
     userInfo.value = user
     showModal.value = show
-    isLoading.value = loading
+    if (show) setDataApi()
 })
 
+//处理数据
+const setDataApi = () => {
+    tabsKey.value = props.tabKey
+}
+
+//tab
+const tabsKey = ref('1')
+const tabsData = ref([
+    { key: '1', name: '个人认证' },
+    { key: '2', name: '公章认证' },
+])
+const tabsChange = ({ key }) => {
+    tabsKey.value = key
+}
+
 //返回的验证码
 const resCode = ref('')
 //表单
@@ -149,18 +182,54 @@ const cancelClick = () => {
 }
 
 //确认
+const isLoading = ref(false)
 const confirmClick = async () => {
-    const validate = await formValidate(reportFormRef.value)
-    if (validate) {
-        saveSmsTimeoutApi().then()
-        emit('confirm')
+    if (tabsKey.value === '1') {
+        const validate = await formValidate(reportFormRef.value)
+        if (validate) {
+            await saveSmsTimeoutApi()
+            emit('confirm', true)
+        }
+    } else if (tabsKey.value === '2') {
+        if (isUKey.value) {
+            emit('confirm', false)
+        } else {
+            window?.$message?.warning('请先连接UKey')
+        }
     }
 }
 
 //验证码过期时间
 const saveSmsTimeoutApi = async () => {
+    isLoading.value = true
     await saveSmsTimeout({
         code: resCode.value,
     })
+    isLoading.value = false
+}
+
+//连接UKey
+const linkUkeyClick = () => {
+    navigator.usb.requestDevice({
+        filters: [{ vendorId: 10367 }],
+    }).then(data => {
+        if (data.manufacturerName === 'CFIST') {
+            userStore.setIsUKey(true)
+            isUKey.value = true
+        }
+    }).catch(() => {})
 }
 </script>
+
+<style lang="scss">
+.el-overlay-dialog .el-dialog.hc-new-dialog.hc-sms-auth-dialog {
+    --el-dialog-padding-primary: 2px;
+    .el-dialog__header {
+        display: none;
+    }
+    .el-card.hc-card-box.hc-new-card-box .hc-card-action.is-action-btn {
+        border-top: 1px solid #E9E9E9;
+        padding-top: 14px;
+    }
+}
+</style>

+ 7 - 0
src/store/index.js

@@ -46,6 +46,7 @@ export const useAppStore = defineStore('main', {
         isSource: getStoreValue('isSource') || '', //来源
         isLayout: getStoreValue('isLayout') || '', //是否显示layout
         isLogin: getStoreValue('isLogin') || false, //是否刚登录
+        isUKey: getStoreValue('isUKey') || false, //是否插入了UKey
     }),
     getters: {
         //系统信息
@@ -84,6 +85,7 @@ export const useAppStore = defineStore('main', {
         getIsSource: state => state.isSource,
         getIsLayout: state => state.isLayout,
         getIsLogin: state => state.isLogin,
+        getIsUKey: state => state.isUKey,
     },
     actions: {
         //系统信息
@@ -220,6 +222,10 @@ export const useAppStore = defineStore('main', {
             this.isLogin = value
             setStoreValue('isLogin', value)
         },
+        setIsUKey(value) {
+            this.isUKey = value
+            setStoreValue('isUKey', value)
+        },
         //清除缓存和token
         clearStoreData() {
             //清除状态
@@ -245,6 +251,7 @@ export const useAppStore = defineStore('main', {
             this.isSource = ''
             this.isLayout = ''
             this.isLogin = null
+            this.isUKey = false
             //清除缓存
             clearStoreAll()
             removeToken()

+ 12 - 15
src/views/tasks/components/TableCard.vue

@@ -2,30 +2,30 @@
     <HcNewCard>
         <template #header>
             <div class="w-32">
-                <el-select v-model="searchForm.typeValue" block clearable placeholder="任务类型">
+                <el-select v-model="searchForm.typeValue" clearable block placeholder="任务类型">
                     <el-option v-for="item in tasksType" :label="item.dictValue" :value="item.dictKey" />
                 </el-select>
             </div>
-            <div class="w-32 ml-3">
-                <el-select v-model="searchForm.statusValue" block clearable placeholder="任务状态">
+            <div class="ml-3 w-32">
+                <el-select v-model="searchForm.statusValue" clearable block placeholder="任务状态">
                     <el-option v-for="item in tasksStatus" :label="item.dictValue" :value="item.dictKey" />
                 </el-select>
             </div>
-            <div class="w-32 ml-3">
-                <el-select v-model="searchForm.contractIdValue" block clearable placeholder="合同段" @change="ContractIdChange">
+            <div class="ml-3 w-32">
+                <el-select v-model="searchForm.contractIdValue" clearable block placeholder="合同段" @change="ContractIdChange">
                     <el-option v-for="item in contractList" :label="item.contractName" :value="item.id" />
                 </el-select>
             </div>
-            <div class="w-32 ml-3">
-                <el-select v-model="searchForm.batch" block clearable placeholder="上报批次">
+            <div class="ml-3 w-32">
+                <el-select v-model="searchForm.batch" clearable block placeholder="上报批次">
                     <el-option v-for="item in reportBatch" :label="item.batch" :value="item.batch" />
                 </el-select>
             </div>
-            <div class="w-64 ml-3">
+            <div class="ml-3 w-64">
                 <HcDatePicker :dates="betweenTime" clearable @change="betweenTimeUpdate" />
             </div>
-            <div class="w-56 ml-3">
-                <el-input v-model="searchForm.queryValue" block clearable placeholder="请输入名称关键词检索" @keyup="keyUpEvent" />
+            <div class="ml-3 w-56">
+                <el-input v-model="searchForm.queryValue" clearable block placeholder="请输入名称关键词检索" @keyup="keyUpEvent" />
             </div>
             <div class="ml-2">
                 <el-button type="primary" @click="searchClick">
@@ -191,7 +191,7 @@ const ContractIdChange = (val) => {
     queryBatchList()
     // queryUserStartFlow()
     getTableData()
-   
+
 }
 
 //获取上报批次
@@ -307,11 +307,8 @@ const queryPage = async () => {
     tableLoading.value = true
     const { error, code, data } = await tasksApi.getTaskPage({
         ...searchForm.value,
-
         projectIdValue: projectId.value,
-
-        currentContractId:currentContractId.value,
-
+        currentContractId: currentContractId.value,
     })
     //处理数据
     tableLoading.value = false

+ 106 - 33
src/views/tasks/hc-data.vue

@@ -41,7 +41,7 @@
             <template #header="{ titleId, titleClass }">
                 <div class="hc-card-header flex items-center">
                     <div :id="titleId" :class="titleClass">任务审核 【已开启电签】</div>
-                    <div v-if="taskReviewType === '1'" class="ml-6 font-bold text-main">
+                    <div v-if="taskReviewType === '1'" class="text-main ml-6 font-bold">
                         任务名称:{{ taskReviewInfo.taskName }}
                     </div>
                 </div>
@@ -91,7 +91,7 @@
 
         <!-- 设置重签规则 -->
         <hc-new-dialog v-model="showSetSignRulesModal" title="设置重签规则" widths="38rem">
-            <div class="text-orange mb-10">
+            <div class="mb-10 text-orange">
                 <span
                     class="mr-4"
                 >提示:设置默认时长,在任务被废除需要重签的时候,规定的重签上报时间提示时间段内,系统提示用户重签信息,但是超过处理时间,系统可默认自动授权重签</span>
@@ -115,13 +115,23 @@
             </template>
         </hc-new-dialog>
 
+        <!-- 短信认证还是公章认证 -->
+        <hc-dialog v-model="isSmsAndUKey" title="确认认证方式" widths="26rem" @close="smsAndUKeyCancel">
+            <div class="mb-6px mt-6px line-height-7">请选择认证方式,是个人短信认证,还是公章认证方式进行签字审批。</div>
+            <template #footer>
+                <el-button size="large" @click="smsAndUKeyCancel">取消操作</el-button>
+                <el-button :loading="smsAuthLoading" hc-btn type="primary" @click="smsAuthClick">个人认证</el-button>
+                <el-button :loading="uKeyAuthLoading" hc-btn type="success" @click="uKeyAuthClick">公章认证</el-button>
+            </template>
+        </hc-dialog>
+
         <!-- 短信认证 -->
-        <HcSmsAuth :loading="SMSAuthLoading" :show="SMSAuthShow" @cancel="SMSAuthCancel" @confirm="SMSAuthConfirm" />
+        <HcSmsAuth :show="SMSAuthShow" :tab-key="smsAuthKey" @cancel="SMSAuthCancel" @confirm="SMSAuthConfirm" />
     </div>
 </template>
 
 <script setup>
-import { onMounted, ref } from 'vue'
+import { onMounted, ref, watch } from 'vue'
 import { useAppStore } from '~src/store'
 import { useRoute, useRouter } from 'vue-router'
 import TableCard from './components/TableCard.vue'
@@ -143,20 +153,28 @@ const activeName = routerQuery?.active || 'key1'
 //全局变量
 const projectId = ref(useAppState.getProjectId)
 const contractId = ref(useAppState.getContractId)
-const projectInfo = ref(useAppState.getProjectInfo)
-
 
 //渲染完成
 onMounted(() => {
     checkSmsCode()
     getContractInfoList()
+    setTimeout(() => {
+        showTaskReviewModal.value = true
+    }, 2000)
 })
+
+//Ukey是否存在
+const isUKey = ref(useAppState.getIsUKey)
+watch(() => useAppState.getIsUKey, (ukey) => {
+    isUKey.value = ukey
+}, { immediate: true, deep: true })
+
 //合同段信息
 const contractList = ref([])
 
 const getContractInfoList = async ()=>{
     const { error, code, data } = await getContractInfo({
-            pid: projectId.value,
+        pid: projectId.value,
      })
      if (!error && code === 200) {
         contractList.value = getArrValue(data)
@@ -189,6 +207,7 @@ const taskReviewForm = ref({ flag: 'OK', comment: '' })
 const taskReviewColumns = ref([
     { key: 'fileName', name: '文件名称' },
 ])
+
 //任务审核
 const rowTaskName = async (row) => {
     if (row.formDataId) {
@@ -293,28 +312,84 @@ const queryTaskInfo = async (row, taskids) => {
 }
 
 //确认审批
+const isSmsAndUKey = ref(false)
 const ConfirmApprovalClick = async () => {
     const formData = taskReviewForm.value
     if (formData.flag === 'NO' && !formData.comment) {
         window?.$message?.warning('请先输入审核意见')
-    } else {
-        SMSAuthLoading.value = true
-        const { error, code, msg, data } = await checkFlowUserIsExistPfxFile({
-            projectId:projectId.value,
-        }, false)
-        //判断数据
+        return
+    }
+    SMSAuthLoading.value = true
+    const { error, code, msg, data } = await checkFlowUserIsExistPfxFile({
+        projectId: projectId.value,
+    }, false)
+    if (error || code !== 200 || data !== true) {
         SMSAuthLoading.value = false
-        if (!error && code === 200 && data === true) {
-            const ShowAuth = isCheckSmsCodeTime()
-            SMSAuthShow.value = ShowAuth
-            //免短信验证
-            if (!ShowAuth) {
-                SMSAuthConfirm()
-            }
-        } else {
-            window.$message?.warning(msg)
-        }
+        window.$message?.warning(msg)
+        return
     }
+    isSmsAndUKey.value = true
+    SMSAuthLoading.value = false
+}
+
+//个人认证
+const smsAuthKey = ref('1')
+const smsAuthLoading = ref(false)
+const smsAuthClick = async () => {
+    smsAuthLoading.value = true
+    const showAuth = isCheckSmsCodeTime()
+    if (showAuth) {
+        smsAuthKey.value = '1'
+        SMSAuthShow.value = showAuth
+        smsAuthLoading.value = false
+    } else {
+        await SMSAuthConfirm(true)
+        smsAuthLoading.value = false
+    }
+}
+
+//公章认证
+const uKeyAuthLoading = ref(false)
+const uKeyAuthClick = async () => {
+    smsAuthLoading.value = true
+    const isUsb = await getUsbDevices()
+    console.log('isUsb:', isUsb)
+    if (!isUsb) {
+        smsAuthKey.value = '2'
+        SMSAuthShow.value = true
+        smsAuthLoading.value = false
+    } else {
+        await SMSAuthConfirm(false)
+        smsAuthLoading.value = false
+    }
+}
+
+//取消
+const smsAndUKeyCancel = () => {
+    smsAuthKey.value = '1'
+    isSmsAndUKey.value = false
+    smsAuthLoading.value = false
+    uKeyAuthLoading.value = false
+}
+
+//获取Ukey设备
+const getUsbDevices = async () => {
+    return new Promise((resolve) => {
+        navigator.usb.getDevices().then(devices => {
+            console.log(devices)
+            let CFIST = false
+            for (let i = 0; i < devices.length; i++) {
+                if (devices[i].manufacturerName === 'CFIST') {
+                    CFIST = true
+                }
+            }
+            useAppState.setIsUKey(CFIST)
+            isUKey.value = CFIST
+            resolve(CFIST)
+        }).catch(() => {
+            resolve(false)
+        })
+    })
 }
 
 //短信验证有效期
@@ -343,14 +418,18 @@ const isCheckSmsCodeTime = () => {
 //短信验证
 const SMSAuthLoading = ref(false)
 const SMSAuthShow = ref(false)
-const SMSAuthConfirm = () => {
+const SMSAuthConfirm = async (sms = true) => {
     const type = taskReviewType.value
     if (type === '1') {
-        saveCompleteApprovalTask()
+        await saveCompleteApprovalTask()
     } else {
-        batchCompleteApprovalTask()
+        await batchCompleteApprovalTask()
     }
-    checkSmsCode()
+    if (sms) await checkSmsCode()
+    isSmsAndUKey.value = false
+    setTimeout(() => {
+        window?.location?.reload() //刷新页面
+    }, 3000)
 }
 const SMSAuthCancel = () => {
     SMSAuthShow.value = false
@@ -373,9 +452,6 @@ const saveCompleteApprovalTask = async () => {
         SMSAuthShow.value = false
         showTaskReviewModal.value = false
         window?.$message?.success('审批成功')
-        setTimeout(() => {
-            window?.location?.reload() //刷新页面
-        }, 3000)
     } else {
         window?.$message?.warning('审批异常')
     }
@@ -404,9 +480,6 @@ const batchCompleteApprovalTask = async () => {
         SMSAuthShow.value = false
         showTaskReviewModal.value = false
         window?.$message?.success('审批成功')
-        setTimeout(() => {
-            window?.location?.reload() //刷新页面
-        }, 3000)
     } else {
         window?.$message?.warning(msg)
     }