Compare commits
1 Commits
46e3659d98
...
kashi
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b04d82393 |
12
.idea/ks-app-employment-service.iml
generated
12
.idea/ks-app-employment-service.iml
generated
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/ks-app-employment-service.iml" filepath="$PROJECT_DIR$/.idea/ks-app-employment-service.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
92
.idea/workspace.xml
generated
92
.idea/workspace.xml
generated
@@ -1,92 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="d1c3f1c8-c1f7-4e7b-8f95-b53233831e5a" name="更改" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/main.js" beforeDir="false" afterPath="$PROJECT_DIR$/main.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/manifest.json" beforeDir="false" afterPath="$PROJECT_DIR$/manifest.json" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/packageRc/pages/daiban/daiban.vue" beforeDir="false" afterPath="$PROJECT_DIR$/packageRc/pages/daiban/daiban.vue" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/packageRc/pages/index/index.vue" beforeDir="false" afterPath="$PROJECT_DIR$/packageRc/pages/index/index.vue" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/pages.json" beforeDir="false" afterPath="$PROJECT_DIR$/pages.json" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/pages/login/login.vue" beforeDir="false" afterPath="$PROJECT_DIR$/pages/login/login.vue" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/unpackage/dist/cache/.vite/deps/_metadata.json" beforeDir="false" afterPath="$PROJECT_DIR$/unpackage/dist/cache/.vite/deps/_metadata.json" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/unpackage/dist/cache/.vite/deps/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/unpackage/dist/cache/.vite/deps/package.json" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/unpackage/dist/dev/mp-weixin/project.config.json" beforeDir="false" afterPath="$PROJECT_DIR$/unpackage/dist/dev/mp-weixin/project.config.json" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_BRANCH_BY_REPOSITORY">
|
||||
<map>
|
||||
<entry key="$PROJECT_DIR$" value="kashi" />
|
||||
</map>
|
||||
</option>
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectColorInfo">{
|
||||
"customColor": "",
|
||||
"associatedIndex": 5
|
||||
}</component>
|
||||
<component name="ProjectId" id="34d7ujHT03QwPL2xzGtFFa7cAse" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"git-widget-placeholder": "dev",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"ts.external.directory.path": "C:\\Program Files\\JetBrains\\WebStorm 2025.1\\plugins\\javascript-plugin\\jsLanguageServicesImpl\\external",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}</component>
|
||||
<component name="SharedIndexes">
|
||||
<attachedChunks>
|
||||
<set>
|
||||
<option value="bundled-js-predefined-d6986cc7102b-f27c65a3e318-JavaScript-WS-251.23774.424" />
|
||||
</set>
|
||||
</attachedChunks>
|
||||
</component>
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="默认任务">
|
||||
<changelist id="d1c3f1c8-c1f7-4e7b-8f95-b53233831e5a" name="更改" comment="" />
|
||||
<created>1761531820211</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1761531820211</updated>
|
||||
<workItem from="1761531821927" duration="3212000" />
|
||||
<workItem from="1761552146567" duration="49000" />
|
||||
<workItem from="1761553662904" duration="1204000" />
|
||||
<workItem from="1761795269754" duration="1248000" />
|
||||
<workItem from="1761819899814" duration="2588000" />
|
||||
<workItem from="1761873794088" duration="313000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
<component name="Vcs.Log.Tabs.Properties">
|
||||
<option name="TAB_STATES">
|
||||
<map>
|
||||
<entry key="MAIN">
|
||||
<value>
|
||||
<State />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
7
App.vue
7
App.vue
@@ -3,20 +3,17 @@ import { reactive, inject, onMounted } from 'vue';
|
||||
import { onLaunch, onShow, onHide } from '@dcloudio/uni-app';
|
||||
import useUserStore from './stores/useUserStore';
|
||||
import useDictStore from './stores/useDictStore';
|
||||
import { tabbarManager } from './utils/tabbarManager';
|
||||
const { $api, navTo, appendScriptTagElement } = inject('globalFunction');
|
||||
import config from '@/config.js';
|
||||
|
||||
onLaunch((options) => {
|
||||
useUserStore().initSeesionId(); //更新
|
||||
useDictStore().getDictData();
|
||||
// uni.hideTabBar();
|
||||
|
||||
// 先尝试从缓存恢复用户信息
|
||||
// 尝试从缓存恢复用户信息
|
||||
const restored = useUserStore().restoreUserInfo();
|
||||
|
||||
// 用户信息恢复后再初始化自定义tabbar
|
||||
tabbarManager.initTabBar();
|
||||
|
||||
if (restored) {
|
||||
// 如果成功恢复用户信息,验证token是否有效
|
||||
let token = uni.getStorageSync('token') || '';
|
||||
|
||||
@@ -22,4 +22,11 @@ api.queryPlanList = (encodeId) => request.globalRequest(`/StudentProfile/QueryPl
|
||||
api.savePlanList = (data) => request.globalRequest(`/StudentProfile/SavePlanList`,'POST', data, 1)
|
||||
// 获取生涯档案(高校
|
||||
api.getGXWechatStudentProfile = () => request.globalRequest(`/StudentProfile/GetGXWechatStudentProfile`,'GET', {}, 1)
|
||||
|
||||
//获取职业路径职业列表
|
||||
api.queryCareerPath = () => request.globalRequest(`/StudentManage/QueryCareerPath`,'POST', {}, 1)
|
||||
// 获取职业详情,参数encodeId 加密id
|
||||
api.queryPathInfo = (encodeId) => request.globalRequest(`/StudentManage/QueryPathInfo?encodeId=${encodeId}`,'POST', {}, 1)
|
||||
|
||||
|
||||
export default api
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import request from '@/utilB/request.js'
|
||||
|
||||
const api = {}
|
||||
// 获取openid
|
||||
api.getOpenId = (params) => request.globalRequest(`/WishOrder/GetOpenId?code=${params}`,'GET', {}, "")
|
||||
|
||||
// 微信支付
|
||||
api.createWeiXinOrder = (data) => request.globalRequest(`/TenpayOrder/CreateWeiXinOrder`, 'POST', data)
|
||||
|
||||
export default api
|
||||
11
apiB/user.js
11
apiB/user.js
@@ -1,6 +1,17 @@
|
||||
import request from '@/utilB/request.js'
|
||||
|
||||
const api = {}
|
||||
//根据openId,获取token,并判断用户是否已绑定账号
|
||||
api.getAccessTokenAndUser = (params) => request.globalRequest(`/WeChartToken/GetAccessTokenAndUser?openId=${params}`,'GET', {})
|
||||
|
||||
//获取用户token 生涯平台token
|
||||
api.queryWechartToken = (userId,schoolId,userType) => request.globalRequest(`/Auth/QueryWechartToken?userId=${userId}&schoolId=${schoolId}&userType=${userType}`,'GET', {},1,4)
|
||||
// 获取openid
|
||||
api.getOpenId = (params) => request.globalRequest(`/WishOrder/GetOpenId?code=${params}`,'GET', {}, "")
|
||||
|
||||
// 微信支付
|
||||
api.createWeiXinOrder = (data) => request.globalRequest(`/TenpayOrder/CreateWeiXinOrder`, 'POST', data)
|
||||
|
||||
// 用户绑定登录
|
||||
api.userBindLogin = (data) => request.globalRequest(`/user/UserBindLogin`, 'POST', data)
|
||||
//用户解绑账号或切换账号(有返回User, Token话,前端重新绑定到header上)
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import request from '@/utilB/request.js'
|
||||
|
||||
const api = {}
|
||||
|
||||
// 获取个人信息
|
||||
api.getMyWeChartUser = (params) => request.globalRequest(`/WeChartUser/GetMyWeChartUser?openId=${params}`,'GET', {})
|
||||
// 保存个人信息
|
||||
api.saveChartUser = (data) => request.globalRequest(`/WeChartUser/SaveChartUser`, 'POST', data)
|
||||
// 保存反馈信息
|
||||
api.saveWeChartUserFeedback = (data) => request.globalRequest(`/WeChartUser/SaveWeChartUserFeedback`, 'POST', data)
|
||||
// 获取出行人列表
|
||||
api.getResearchUserList = (params) => request.globalRequest(`/WeChartUser/GetResearchUserList?openId=${params}`,'GET', {})
|
||||
// 删除出行人
|
||||
api.deleteResearchUser = (params) => request.globalRequest(`/WeChartUser/DeleteResearchUser?id=${params}`,'GET', {})
|
||||
// 保存出行人
|
||||
api.saveResearchUser = (data) => request.globalRequest(`/WeChartUser/SaveResearchUser`, 'POST', data)
|
||||
|
||||
//根据openId,获取token,并判断用户是否已绑定账号
|
||||
api.getAccessTokenAndUser = (params) => request.globalRequest(`/WeChartToken/GetAccessTokenAndUser?openId=${params}`,'GET', {})
|
||||
|
||||
//获取用户token 生涯平台token
|
||||
api.queryWechartToken = (userId,schoolId,userType) => request.globalRequest(`/Auth/QueryWechartToken?userId=${userId}&schoolId=${schoolId}&userType=${userType}`,'GET', {},1,4)
|
||||
|
||||
api.sLLogin = (accessToken,openId,studentId) => request.globalRequest(`/WeChartUser/SLLogin?accessToken=${accessToken}&openId=${openId}&studentId=${studentId}`, 'POST', {})
|
||||
|
||||
export default api
|
||||
177
apiRc/login.js
177
apiRc/login.js
@@ -1,177 +0,0 @@
|
||||
/*
|
||||
* @Descripttion:
|
||||
* @Author: lip
|
||||
* @Date: 2023-07-27 16:01:59
|
||||
* @LastEditors: shirlwang
|
||||
*/
|
||||
import request from '@/utilsRc/request'
|
||||
|
||||
// 登录方法
|
||||
// export function login(data) {
|
||||
// return request({
|
||||
// 'url': '/login',
|
||||
// headers: {
|
||||
// isToken: false
|
||||
// },
|
||||
// 'method': 'post',
|
||||
// data
|
||||
// })
|
||||
// }
|
||||
const userApi = {
|
||||
Login: '/login',
|
||||
Logout: '/logout',
|
||||
Register: '/register',
|
||||
// get my info
|
||||
UserInfo: '/getInfo'
|
||||
}
|
||||
// 登录接口
|
||||
export function login(data) {
|
||||
return request({
|
||||
'url': '/personnel/personBaseInfo/loginGrAndQy',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
'method': 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function loginphone(data) {
|
||||
return request({
|
||||
'url': '/not/login/person/xcxLogin',
|
||||
'method': 'get',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 注册方法
|
||||
export function register (data) {
|
||||
return request({
|
||||
'url': '/register',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 短信登录
|
||||
export function smsLogin(data) {
|
||||
return request({
|
||||
'url': '/smsLogin',
|
||||
'method': 'post',
|
||||
'data': data
|
||||
})
|
||||
}
|
||||
export function wechatLogin(data) {
|
||||
return request({
|
||||
url: '/xcxLogin',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取用户详细信息
|
||||
export function getInfo() {
|
||||
return request({
|
||||
'url': '/getInfo',
|
||||
'method': 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 退出方法
|
||||
export function logout() {
|
||||
return request({
|
||||
'url': '/logout',
|
||||
'method': 'post'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取验证码
|
||||
export function getCodeImg() {
|
||||
return request({
|
||||
'url': '/captchaImage',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
method: 'get',
|
||||
timeout: 20000
|
||||
})
|
||||
}
|
||||
|
||||
export function registerReq(data) {
|
||||
// const data = {
|
||||
// username,
|
||||
// password,
|
||||
// code,
|
||||
// uuid,
|
||||
// userType
|
||||
// }
|
||||
return request({
|
||||
'url': '/registerBody',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
'method': 'post',
|
||||
'data': data
|
||||
})
|
||||
}
|
||||
|
||||
// 发送邮箱验证码
|
||||
export function sendMailCode(query) {
|
||||
return request({
|
||||
url: '/manage/mail/sendMailCode',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 发送短信(人才集团)
|
||||
export function sendSmsForRcjt(data) {
|
||||
return request({
|
||||
'url': '/sendSmsForRcjt',
|
||||
'method': 'post',
|
||||
'data': data
|
||||
})
|
||||
}
|
||||
// 发送登录短信
|
||||
export function sendSmsCode(data) {
|
||||
return request({
|
||||
'url': '/onlySendSms',
|
||||
'method': 'post',
|
||||
'data': data
|
||||
})
|
||||
}
|
||||
// 修改密码
|
||||
export function updateUserPwd({oldPassword, newPassword}) {
|
||||
return request({
|
||||
url: '/system/center/user/profile/updatePwd',
|
||||
method: 'put',
|
||||
params: {
|
||||
oldPassword,
|
||||
newPassword
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 找回密码 发送验证码
|
||||
export function sendSms(data) {
|
||||
return request({
|
||||
'url': '/sendSms',
|
||||
'method': 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 找回密码
|
||||
export function retrieve(data) {
|
||||
return request({
|
||||
'url': '/retrieve',
|
||||
'method': 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* @Date: 2025-10-31 13:50:15
|
||||
* @LastEditors: shirlwang
|
||||
* @LastEditTime: 2025-10-31 14:30:31
|
||||
*/
|
||||
import request from '@/utilsRc/request'
|
||||
|
||||
// 人员信息保存
|
||||
export function savePersonBase(data) {
|
||||
return request({
|
||||
'url': '/personnel/personBaseInfo',
|
||||
'method': 'put',
|
||||
'data': data
|
||||
})
|
||||
}
|
||||
|
||||
// 人员信息查询
|
||||
export function getPersonBase(userId) {
|
||||
return request({
|
||||
'url': `/personnel/personBaseInfo/user/${userId}`,
|
||||
'method': 'get',
|
||||
})
|
||||
}
|
||||
// 获取行政区划列表
|
||||
export function getQUList() {
|
||||
return request({
|
||||
url: `/manage/xzqh//xzqhTree`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
// 查询部门下拉树结构
|
||||
export function deptTreeSelect() {
|
||||
return request({
|
||||
url: '/system/center/user/deptTree',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 社群端 根据所在社区 获取姓名
|
||||
export function generateUserName(deptId) {
|
||||
return request({
|
||||
url: `/generateUserName/${deptId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取部门列表
|
||||
export function getDeptList(name,personId) {
|
||||
return request({
|
||||
url: `/system/center/user/getDeptList?name=${name}&parentId=${personId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
// 求职工种列表
|
||||
export function touristWork() {
|
||||
return request({
|
||||
url: `/basicdata/workType/workTypeTree`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
// 获取招聘工种列表
|
||||
export function jobTypeList(params) {
|
||||
return request({
|
||||
url: '/basicdata/workType/list',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
// 未读消息数量
|
||||
|
||||
export function unreadNum() {
|
||||
return request({
|
||||
url: '/manage/tjgw/notReadNum',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
// 地图类型列表
|
||||
export function jyshdt(cyfhjd) {
|
||||
return request({
|
||||
url: `/jyshdt/jyshdt/queryList?lx=${cyfhjd}`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import request from '@/utilsRc/request'
|
||||
// 查询字典数据列表
|
||||
export function listData (query) {
|
||||
return request({
|
||||
url: '/system/dict/data/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询字典数据详细
|
||||
export function getData (dictCode) {
|
||||
return request({
|
||||
url: '/system/dict/data/' + dictCode,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 根据字典类型查询字典数据信息
|
||||
export function getDicts (dictType) {
|
||||
return request({
|
||||
url: '/system/dict/data/type/' + dictType,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增字典数据
|
||||
export function addData (data) {
|
||||
return request({
|
||||
url: '/system/dict/data/add',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改字典数据
|
||||
export function updateData (data) {
|
||||
return request({
|
||||
url: '/system/dict/data',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除字典数据
|
||||
export function delData (dictCode) {
|
||||
return request({
|
||||
url: '/system/dict/data/remove' + dictCode,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
@@ -2,8 +2,7 @@ import useUserStore from "../stores/useUserStore";
|
||||
import {
|
||||
request,
|
||||
createRequest,
|
||||
uploadFile,
|
||||
myRequest
|
||||
uploadFile
|
||||
} from "../utils/request";
|
||||
import streamRequest, {
|
||||
chatRequest
|
||||
@@ -886,8 +885,7 @@ export const $api = {
|
||||
uploadFile,
|
||||
formatFileSize,
|
||||
sendingMiniProgramMessage,
|
||||
copyText,
|
||||
myRequest
|
||||
copyText
|
||||
}
|
||||
|
||||
|
||||
@@ -918,4 +916,4 @@ export default {
|
||||
insertSortData,
|
||||
isInWechatMiniProgramWebview,
|
||||
isEmptyObject,
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@
|
||||
>
|
||||
<!-- 顶部头部区域 -->
|
||||
<view
|
||||
v-if="title"
|
||||
class="container-header"
|
||||
:style="border ? { borderBottom: `2rpx solid ${borderColor}` } : { borderBottom: 'none' }"
|
||||
>
|
||||
@@ -50,7 +49,7 @@ const emit = defineEmits(['onScrollBottom']);
|
||||
defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
default: '标题',
|
||||
},
|
||||
border: {
|
||||
type: Boolean,
|
||||
|
||||
@@ -1,337 +0,0 @@
|
||||
<template>
|
||||
<view class="custom-tabbar">
|
||||
<view
|
||||
class="tabbar-item"
|
||||
v-for="(item, index) in tabbarList"
|
||||
:key="index"
|
||||
@click="switchTab(item, index)"
|
||||
>
|
||||
<view class="tabbar-icon">
|
||||
<image
|
||||
:src="currentItem === item.id ? item.selectedIconPath : item.iconPath"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<view class="badge" v-if="item.badge && item.badge > 0">{{ item.badge }}</view>
|
||||
<view class="tabbar-text" :class="{ 'active': currentItem === item.id }">
|
||||
{{ item.text }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch, onMounted } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import useUserStore from '@/stores/useUserStore';
|
||||
import { useReadMsg } from '@/stores/useReadMsg';
|
||||
|
||||
const props = defineProps({
|
||||
currentPage: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
});
|
||||
|
||||
const userStore = useUserStore();
|
||||
const { userInfo } = storeToRefs(userStore);
|
||||
const readMsg = useReadMsg();
|
||||
const currentItem = ref(props.currentPage);
|
||||
|
||||
// 监听props变化
|
||||
watch(() => props.currentPage, (newPage) => {
|
||||
currentItem.value = newPage;
|
||||
});
|
||||
|
||||
// 生成tabbar配置的函数
|
||||
const generateTabbarList = () => {
|
||||
const baseItems = [
|
||||
{
|
||||
id: 0,
|
||||
text: '职位',
|
||||
path: '/pages/index/index',
|
||||
iconPath: '/static/tabbar/calendar.png',
|
||||
selectedIconPath: '/static/tabbar/calendared.png',
|
||||
centerItem: false,
|
||||
badge: readMsg.badges[0]?.count || 0,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
text: 'AI+',
|
||||
path: '/pages/chat/chat',
|
||||
iconPath: '/static/tabbar/logo3.png',
|
||||
selectedIconPath: '/static/tabbar/logo3.png',
|
||||
centerItem: true,
|
||||
badge: readMsg.badges[2]?.count || 0,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
text: '消息',
|
||||
path: '/pages/msglog/msglog',
|
||||
iconPath: '/static/tabbar/chat4.png',
|
||||
selectedIconPath: '/static/tabbar/chat4ed.png',
|
||||
centerItem: false,
|
||||
badge: readMsg.badges[3]?.count || 0,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
text: '我的',
|
||||
path: '/pages/mine/mine',
|
||||
iconPath: '/static/tabbar/mine.png',
|
||||
selectedIconPath: '/static/tabbar/mined.png',
|
||||
centerItem: false,
|
||||
badge: readMsg.badges[4]?.count || 0,
|
||||
},
|
||||
];
|
||||
|
||||
// 获取用户类型,统一使用isCompanyUser字段(0=企业用户,1=求职者, 3=网格员)
|
||||
// 优先从store获取,如果为空则直接从缓存获取
|
||||
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
|
||||
|
||||
// 获取isCompanyUser字段
|
||||
const storeIsCompanyUser = userInfo.value?.isCompanyUser;
|
||||
const cachedIsCompanyUser = cachedUserInfo.isCompanyUser;
|
||||
|
||||
// 获取用户类型的逻辑:
|
||||
// 1. 优先使用store中的isCompanyUser
|
||||
// 2. 如果store中没有,使用缓存中的isCompanyUser
|
||||
// 3. 最后默认为1(求职者)
|
||||
const userType = Number(storeIsCompanyUser !== undefined ? storeIsCompanyUser : (cachedIsCompanyUser !== undefined ? cachedIsCompanyUser : 1));
|
||||
if (userType === 0 || userType === 2) {
|
||||
// 企业用户:显示发布岗位
|
||||
baseItems.splice(1, 0, {
|
||||
id: 1,
|
||||
text: '发布岗位',
|
||||
path: '/pages/job/publishJob',
|
||||
iconPath: '/static/tabbar/post.png',
|
||||
selectedIconPath: '/static/tabbar/posted.png',
|
||||
centerItem: false,
|
||||
badge: 0,
|
||||
});
|
||||
} else {
|
||||
// 求职者用户(包括未登录状态):显示招聘会
|
||||
baseItems.splice(1, 0, {
|
||||
id: 1,
|
||||
text: '招聘会',
|
||||
path: '/pages/careerfair/careerfair',
|
||||
iconPath: '/static/tabbar/post.png',
|
||||
selectedIconPath: '/static/tabbar/posted.png',
|
||||
centerItem: false,
|
||||
badge: readMsg.badges[1]?.count || 0,
|
||||
});
|
||||
}
|
||||
|
||||
return baseItems;
|
||||
};
|
||||
|
||||
// 根据用户类型生成不同的导航栏配置
|
||||
const tabbarList = computed(() => {
|
||||
return generateTabbarList();
|
||||
});
|
||||
|
||||
// 强制刷新tabbar的方法
|
||||
const forceRefresh = () => {
|
||||
// 触发响应式更新
|
||||
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
|
||||
const currentUserType = userInfo.value?.isCompanyUser !== undefined ? userInfo.value.isCompanyUser : (cachedUserInfo.isCompanyUser !== undefined ? cachedUserInfo.isCompanyUser : 1);
|
||||
};
|
||||
|
||||
// 监听用户类型变化(只监听isCompanyUser字段)
|
||||
watch(() => userInfo.value?.isCompanyUser, (newIsCompanyUser, oldIsCompanyUser) => {
|
||||
if (newIsCompanyUser !== oldIsCompanyUser) {
|
||||
// 强制触发computed重新计算
|
||||
forceRefresh();
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
// 监听用户信息变化(包括登录状态)
|
||||
watch(() => userInfo.value, (newUserInfo, oldUserInfo) => {
|
||||
if (newUserInfo !== oldUserInfo) {
|
||||
// 强制触发computed重新计算
|
||||
forceRefresh();
|
||||
}
|
||||
}, { immediate: true, deep: true });
|
||||
|
||||
// 切换tab
|
||||
const switchTab = (item, index) => {
|
||||
// 检查是否为"发布岗位"页面,需要判断企业信息是否完整
|
||||
if (item.path === '/pages/job/publishJob') {
|
||||
// 检查用户是否已登录
|
||||
const token = uni.getStorageSync('token') || '';
|
||||
const hasLogin = userStore.hasLogin;
|
||||
|
||||
if (!token || !hasLogin) {
|
||||
// 未登录,发送事件显示登录弹窗
|
||||
uni.$emit('showLoginModal');
|
||||
return; // 不进行页面跳转
|
||||
}
|
||||
|
||||
// 已登录,检查企业信息是否完整
|
||||
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
|
||||
const storeUserInfo = userInfo.value || {};
|
||||
const currentUserInfo = storeUserInfo.id ? storeUserInfo : cachedUserInfo;
|
||||
|
||||
// 判断企业信息字段company是否为null或undefined
|
||||
if (!currentUserInfo.company || currentUserInfo.company === null) {
|
||||
// 企业信息为空,跳转到企业信息补全页面
|
||||
uni.navigateTo({
|
||||
url: '/pages/complete-info/company-info',
|
||||
});
|
||||
} else {
|
||||
// 企业信息完整,跳转到发布岗位页面
|
||||
uni.navigateTo({
|
||||
url: '/pages/job/publishJob',
|
||||
});
|
||||
}
|
||||
|
||||
currentItem.value = item.id;
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否为"我的"页面,需要登录验证和用户类型判断
|
||||
if (item.path === '/pages/mine/mine') {
|
||||
// 检查用户是否已登录
|
||||
const token = uni.getStorageSync('token') || '';
|
||||
const hasLogin = userStore.hasLogin;
|
||||
|
||||
if (!token || !hasLogin) {
|
||||
// 未登录,发送事件显示登录弹窗
|
||||
uni.$emit('showLoginModal');
|
||||
return; // 不进行页面跳转
|
||||
}
|
||||
|
||||
// 已登录,根据用户类型跳转到不同的"我的"页面
|
||||
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
|
||||
const storeIsCompanyUser = userInfo.value?.isCompanyUser;
|
||||
const cachedIsCompanyUser = cachedUserInfo.isCompanyUser;
|
||||
|
||||
// 获取用户类型
|
||||
const userType = Number(storeIsCompanyUser !== undefined ? storeIsCompanyUser : (cachedIsCompanyUser !== undefined ? cachedIsCompanyUser : 1));
|
||||
|
||||
let targetPath = '/pages/mine/mine'; // 默认求职者页面
|
||||
|
||||
if (userType === 0) {
|
||||
// 企业用户,跳转到企业我的页面
|
||||
targetPath = '/pages/mine/company-mine';
|
||||
} else {
|
||||
// 求职者或其他用户类型,跳转到普通我的页面
|
||||
targetPath = '/pages/mine/mine';
|
||||
}
|
||||
|
||||
// 跳转到对应的页面
|
||||
uni.navigateTo({
|
||||
url: targetPath,
|
||||
});
|
||||
|
||||
currentItem.value = item.id;
|
||||
return;
|
||||
}
|
||||
|
||||
// 判断是否为 tabBar 页面
|
||||
const tabBarPages = [
|
||||
'/pages/index/index',
|
||||
'/pages/careerfair/careerfair',
|
||||
'/pages/chat/chat',
|
||||
'/pages/msglog/msglog',
|
||||
'/pages/mine/mine'
|
||||
];
|
||||
|
||||
if (tabBarPages.includes(item.path)) {
|
||||
// TabBar 页面使用 redirectTo 避免页面栈溢出
|
||||
uni.redirectTo({
|
||||
url: item.path,
|
||||
});
|
||||
} else {
|
||||
// 非 TabBar 页面使用 navigateTo
|
||||
uni.navigateTo({
|
||||
url: item.path,
|
||||
});
|
||||
}
|
||||
|
||||
currentItem.value = item.id;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
currentItem.value = props.currentPage;
|
||||
// 调试信息:显示当前用户状态和tabbar配置
|
||||
forceRefresh();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.custom-tabbar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 88rpx;
|
||||
background-color: #ffffff;
|
||||
border-top: 1rpx solid #e5e5e5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
z-index: 999;
|
||||
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.tabbar-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
color: #5E5F60;
|
||||
font-size: 22rpx;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tabbar-icon {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
margin-bottom: 4rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tabbar-icon image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tabbar-text {
|
||||
font-size: 20rpx;
|
||||
line-height: 1;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.tabbar-text.active {
|
||||
color: #256BFA;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.badge {
|
||||
position: absolute;
|
||||
top: 4rpx;
|
||||
right: 20rpx;
|
||||
min-width: 30rpx;
|
||||
height: 30rpx;
|
||||
background-color: #ff4444;
|
||||
color: #fff;
|
||||
font-size: 18rpx;
|
||||
border-radius: 15rpx;
|
||||
text-align: center;
|
||||
line-height: 30rpx;
|
||||
padding: 0 10rpx;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
/* 中间按钮特殊样式 */
|
||||
.tabbar-item:has(.center-item) {
|
||||
.tabbar-icon {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,158 +0,0 @@
|
||||
<template>
|
||||
<view class="upload-container">
|
||||
<u-upload :disabled="disabled" :width="width" :height="height" :fileList="internalFileList" :name="name" :multiple="multiple"
|
||||
:maxCount="maxCount" @afterRead="handleAfterRead" @delete="handleRemove">
|
||||
</u-upload>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import {
|
||||
// uploadImg
|
||||
// } from '@/api/company'
|
||||
import config from '@/config'
|
||||
//import {
|
||||
// getToken
|
||||
//} from '@/utils/auth'
|
||||
export default {
|
||||
props: {
|
||||
maxSize: {
|
||||
type: Number,
|
||||
default: 5, // 最大文件大小(MB)
|
||||
},
|
||||
allowedFormats: {
|
||||
type: Array,
|
||||
default: () => [], // 允许的文件格式
|
||||
},
|
||||
maxImageSize: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
width: 2048,
|
||||
height: 2048
|
||||
}), // 图片最大宽度和高度
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100rpx', // 默认宽度
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '100rpx', // 默认高度
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: 'file', // 默认name字段
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false, // 是否允许多选,默认不允许
|
||||
},
|
||||
maxCount: {
|
||||
type: Number,
|
||||
default: 1, // 默认最大上传数量为1
|
||||
},
|
||||
fileList: {
|
||||
type: Array,
|
||||
default: () => [], // 默认的文件列表为空
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
internalFileList: [...this.fileList], // 内部的文件列表,确保与父组件的同步
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
// 监听 fileList 的变化,确保内外部数据同步
|
||||
fileList(newVal) {
|
||||
this.internalFileList = [...newVal];
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// 新增图片
|
||||
async handleAfterRead(event) {
|
||||
let lists = [].concat(event.file);
|
||||
let fileListLen = this.internalFileList.length;
|
||||
lists.map((item) => {
|
||||
this.internalFileList.push({
|
||||
...item,
|
||||
status: "uploading",
|
||||
message: "上传中",
|
||||
});
|
||||
});
|
||||
for (let i = 0; i < lists.length; i++) {
|
||||
if (this.allowedFormats.length > 0) {
|
||||
let fileType = lists[i].name.split('.').pop().toLowerCase();
|
||||
if (!this.allowedFormats.includes(fileType)) {
|
||||
// this.$emit('error', '不支持的文件格式');
|
||||
uni.showToast({
|
||||
title: '不支持的文件格式',
|
||||
icon: 'none',
|
||||
});
|
||||
this.internalFileList.splice(fileListLen, 1);
|
||||
this.$emit('update', this.internalFileList); // 通知父组件文件列表更新
|
||||
return;
|
||||
}
|
||||
}
|
||||
const result = await this.uploadFilePromise(lists[i].url);
|
||||
let item = this.internalFileList[fileListLen];
|
||||
this.internalFileList.splice(
|
||||
fileListLen,
|
||||
1,
|
||||
Object.assign(item, {
|
||||
status: "success",
|
||||
message: "",
|
||||
data: result,
|
||||
})
|
||||
);
|
||||
fileListLen++;
|
||||
this.$emit('update', this.internalFileList); // 通知父组件文件列表更新
|
||||
}
|
||||
},
|
||||
uploadFilePromise(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.uploadFile({
|
||||
url: config.baseUrl + '/system/oss/upload',
|
||||
filePath: url,
|
||||
name: "file",
|
||||
header: {
|
||||
Authorization: "Bearer " + getToken(),
|
||||
},
|
||||
success: (uploadFileRes) => {
|
||||
let res = JSON.parse(uploadFileRes.data);
|
||||
resolve(res.data);
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log(err);
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
handleRemove({file, index}) {
|
||||
this.internalFileList.splice(index, 1); // 从文件列表中移除指定文件
|
||||
this.$emit('update', this.internalFileList); // 通知父组件文件列表更新
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.upload-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.upload-slot {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px dashed #ccc;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
@@ -31,13 +31,13 @@ const userTypes = [
|
||||
{ value: 3, label: '政府人员' }
|
||||
];
|
||||
|
||||
const currentUserType = computed(() => userInfo.value?.isCompanyUser !== undefined ? userInfo.value.isCompanyUser : 0);
|
||||
const currentUserType = computed(() => userInfo.value?.userType || 0);
|
||||
|
||||
const switchUserType = (userType) => {
|
||||
console.log('切换用户类型:', userType);
|
||||
console.log('切换前 userInfo:', userInfo.value);
|
||||
|
||||
userInfo.value.isCompanyUser = userType;
|
||||
userInfo.value.userType = userType;
|
||||
|
||||
console.log('切换后 userInfo:', userInfo.value);
|
||||
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
<template>
|
||||
<view>
|
||||
<u-popup :show="visible">
|
||||
<view style="position: relative;height: 100vh;width: 100%;">
|
||||
<view class="button-area" style="padding: 8vh 32rpx 24rpx 32rpx; margin: 0;border: 0;position: relative;z-index: 2;display: block; top: 10rpx; border-radius: 0;">
|
||||
<u-input style="margin-bottom: 16px;" v-model="placeInput" @change="getLocations" placeholder="请输入并选择相应地点"></u-input>
|
||||
<view v-if="checkedMarker&&checkedMarker.name" class="selected">您已选择:{{ checkedMarker.name }}<text v-if="checkedMarker.address">({{ checkedMarker.address }})</text></view>
|
||||
<view v-for="(item, index) in placeList" :key="index" :label="item.name" :value="item.name" @click="addIcon(item.name)" class="place-list">
|
||||
<view style="display: flex;justify-content: space-between;">{{ item.name }}
|
||||
<view style="color: #8492a6; font-size: 13px;width: 50%">{{ item.address }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="map" id="map"></view>
|
||||
<view class="button-area">
|
||||
<view class="btn" @click="cancel">取 消</view>
|
||||
<view class="btn save" @click="submitForm">确 定</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
//import { jsonp } from "vue-jsonp";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
map: '',
|
||||
placeList: [],
|
||||
placeInput: '',
|
||||
markerList: [],
|
||||
checkedMarker: '',
|
||||
visible: false,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// this.openDialog();
|
||||
},
|
||||
methods: {
|
||||
submitForm() {
|
||||
if(this.checkedMarker&&this.checkedMarker.name){
|
||||
this.$emit('selected', this.checkedMarker)
|
||||
this.visible = false;
|
||||
}else{
|
||||
this.$message.warning('您尚未选择地点!')
|
||||
}
|
||||
},
|
||||
cancel() {
|
||||
this.visible = false;
|
||||
if (this.map) {
|
||||
this.map.removeEventListener('click', this.handleMapClick);
|
||||
}
|
||||
},
|
||||
openDialog() {
|
||||
this.visible = true;
|
||||
this.$nextTick(() => {
|
||||
this.map = new BMapGL.Map("map");
|
||||
var point = new BMapGL.Point(117.123237,36.657017);
|
||||
this.map.centerAndZoom(point, 15);
|
||||
this.map.enableScrollWheelZoom();
|
||||
var locationCtrl = new BMapGL.LocationControl();
|
||||
this.map.addControl(locationCtrl)
|
||||
this.map.addEventListener('click', this.handleMapClick);
|
||||
})
|
||||
},
|
||||
handleMapClick(e) {
|
||||
const lng = e.latlng.lng;
|
||||
const lat = e.latlng.lat;
|
||||
// 逆地理编码
|
||||
jsonp("https://api.map.baidu.com/reverse_geocoding/v3/", {
|
||||
ak: "qr93Dm5Ph6Vb4n1aTfvHG9KZkvG8S4YU",
|
||||
output: "json",
|
||||
location: `${lat},${lng}`,
|
||||
}).then(res => {
|
||||
if (res.status === 0) {
|
||||
// 清除原有标记
|
||||
this.removeOverlay();
|
||||
// 新建标记
|
||||
const point = new BMapGL.Point(lng, lat);
|
||||
const marker = new BMapGL.Marker(point);
|
||||
this.map.addOverlay(marker);
|
||||
this.markerList = [marker];
|
||||
// 保存选中信息
|
||||
this.checkedMarker = {
|
||||
name: res.result.formatted_address,
|
||||
address: res.result.sematic_description,
|
||||
location: { lng, lat }
|
||||
};
|
||||
this.$forceUpdate();
|
||||
}
|
||||
});
|
||||
},
|
||||
getLocations(place) {
|
||||
jsonp("https://api.map.baidu.com/place/v2/suggestion", {
|
||||
q: place,
|
||||
ak: "qr93Dm5Ph6Vb4n1aTfvHG9KZkvG8S4YU",
|
||||
region: '济南市',
|
||||
output: "json",
|
||||
})
|
||||
.then((json) => {
|
||||
console.log(json,23423434)
|
||||
this.placeList = json.result
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
addIcon(e) {
|
||||
let arr = this.placeList.filter(ele => ele.name == e)
|
||||
if(arr.length){
|
||||
this.addMaker(arr)
|
||||
this.clickMaker(arr[0]);
|
||||
}else{
|
||||
this.addMaker(JSON.parse(JSON.stringify(this.placeList)))
|
||||
}
|
||||
this.placeList = []
|
||||
},
|
||||
removeOverlay() {
|
||||
this.markerList.forEach(ele => {
|
||||
this.map.removeOverlay(ele)
|
||||
})
|
||||
},
|
||||
addMaker(list) {
|
||||
this.removeOverlay()
|
||||
list.forEach((ele, index) => {
|
||||
let point = new BMapGL.Point(ele.location.lng, ele.location.lat);
|
||||
if(index == 0){
|
||||
this.map.centerAndZoom(point, 15)
|
||||
}
|
||||
let marker = new BMapGL.Marker(point); // 创建标注
|
||||
this.map.addOverlay(marker);
|
||||
let that = this;
|
||||
this.markerList.push(marker)
|
||||
marker.addEventListener("click", function(){
|
||||
that.clickMaker(ele)
|
||||
});
|
||||
})
|
||||
},
|
||||
clickMaker(e){
|
||||
this.checkedMarker = e;
|
||||
this.$forceUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.selected {
|
||||
margin-bottom: 16px;
|
||||
position: relative;z-index: 2;
|
||||
background: #DCE2E9;
|
||||
border-radius: 8rpx;
|
||||
padding: 12rpx 24rpx;
|
||||
}
|
||||
.map{
|
||||
width: 100%;
|
||||
margin-top: 16px;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.button-area{
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
bottom: 0;
|
||||
padding: 24rpx 32rpx 68rpx;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
margin-top: 40rpx;
|
||||
border-radius: 16px 16px 0px 0px;
|
||||
.btn{
|
||||
line-height: 72rpx;
|
||||
width: 176rpx;
|
||||
margin-right: 16rpx;
|
||||
font-size: 28rpx;
|
||||
border: 1px solid #B8C5D4;
|
||||
color: #282828;
|
||||
text-align: center;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
.reset{
|
||||
background: #DCE2E9;
|
||||
}
|
||||
.save{
|
||||
background: linear-gradient(103deg, #1D64CF 0%, #1590D4 99%);
|
||||
color: #fff;
|
||||
border: 0;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
.place-list{
|
||||
line-height: 32rpx;
|
||||
padding: 16rpx 0;
|
||||
border-bottom: 1px solid #DCE2E9;
|
||||
}
|
||||
</style>
|
||||
@@ -13,30 +13,7 @@
|
||||
<view class="btn-confirm" @click="confirm">确认</view>
|
||||
</view>
|
||||
<view class="popup-list">
|
||||
<!-- 多选模式 -->
|
||||
<view v-if="multiSelect" class="multi-select-list">
|
||||
<view v-if="!processedListData[0] || processedListData[0].length === 0" class="empty-tip">
|
||||
暂无数据
|
||||
</view>
|
||||
<view
|
||||
v-else
|
||||
class="skill-tags-container"
|
||||
>
|
||||
<view
|
||||
v-for="(item, index) in processedListData[0]"
|
||||
:key="index"
|
||||
class="skill-tag"
|
||||
:class="{ 'skill-tag-active': selectedValues.includes(item[this.rowKey]) }"
|
||||
@click.stop="toggleSelect(item)"
|
||||
@touchstart.stop="toggleSelect(item)"
|
||||
>
|
||||
<text class="skill-tag-text">{{ getLabel(item) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 单选模式 -->
|
||||
<picker-view
|
||||
v-else
|
||||
indicator-style="height: 84rpx;"
|
||||
:value="selectedIndex"
|
||||
@change="bindChange"
|
||||
@@ -77,8 +54,6 @@ export default {
|
||||
rowKey: 'value',
|
||||
selectedItems: [],
|
||||
unit: '',
|
||||
multiSelect: false,
|
||||
selectedValues: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -91,13 +66,6 @@ export default {
|
||||
});
|
||||
});
|
||||
},
|
||||
// 计算选中的项目
|
||||
computedSelectedItems() {
|
||||
if (!this.multiSelect) return this.selectedItems;
|
||||
return this.processedListData[0] ? this.processedListData[0].filter(item =>
|
||||
this.selectedValues.includes(item[this.rowKey])
|
||||
) : [];
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
open(newConfig = {}) {
|
||||
@@ -112,10 +80,7 @@ export default {
|
||||
rowKey = 'value',
|
||||
maskClick = false,
|
||||
defaultIndex = [],
|
||||
multiSelect = false,
|
||||
defaultValues = [],
|
||||
} = newConfig;
|
||||
|
||||
this.reset();
|
||||
if (title) this.title = title;
|
||||
if (typeof success === 'function') this.confirmCallback = success;
|
||||
@@ -127,16 +92,10 @@ export default {
|
||||
this.rowKey = rowKey;
|
||||
this.maskClick = maskClick;
|
||||
this.unit = unit;
|
||||
this.multiSelect = multiSelect;
|
||||
|
||||
if (multiSelect) {
|
||||
this.selectedValues = defaultValues || [];
|
||||
} else {
|
||||
this.selectedIndex =
|
||||
defaultIndex.length === this.listData.length ? defaultIndex : new Array(this.listData.length).fill(0);
|
||||
this.selectedItems = this.selectedIndex.map((val, index) => this.processedListData[index][val]);
|
||||
}
|
||||
|
||||
this.selectedIndex =
|
||||
defaultIndex.length === this.listData.length ? defaultIndex : new Array(this.listData.length).fill(0);
|
||||
this.selectedItems = this.selectedIndex.map((val, index) => this.processedListData[index][val]);
|
||||
this.$nextTick(() => {
|
||||
this.$refs.popup.open();
|
||||
});
|
||||
@@ -158,22 +117,6 @@ export default {
|
||||
getLabel(item) {
|
||||
return item?.[this.rowLabel] ?? '';
|
||||
},
|
||||
toggleSelect(item) {
|
||||
if (!item || !this.rowKey || !item[this.rowKey]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const value = item[this.rowKey];
|
||||
const index = this.selectedValues.indexOf(value);
|
||||
|
||||
if (index > -1) {
|
||||
// 取消选中
|
||||
this.selectedValues.splice(index, 1);
|
||||
} else {
|
||||
// 选中
|
||||
this.selectedValues.push(value);
|
||||
}
|
||||
},
|
||||
setColunm(index, list) {
|
||||
if (index > this.listData.length) {
|
||||
return console.warn('最长' + this.listData.length);
|
||||
@@ -192,14 +135,7 @@ export default {
|
||||
}
|
||||
|
||||
try {
|
||||
let result;
|
||||
if (this.multiSelect) {
|
||||
// 多选模式:传递 selectedValues 和 selectedItems
|
||||
result = await callback(this.selectedValues, this.computedSelectedItems);
|
||||
} else {
|
||||
// 单选模式:传递 selectedIndex 和 selectedItems
|
||||
result = await callback(this.selectedIndex, this.selectedItems);
|
||||
}
|
||||
const result = await callback(this.selectedIndex, this.selectedItems); // 无论是 async 还是返回 Promise 的函数都可以 await
|
||||
if (result !== false) {
|
||||
this.$refs.popup.close();
|
||||
}
|
||||
@@ -218,8 +154,6 @@ export default {
|
||||
this.rowKey = 'value';
|
||||
this.selectedItems = [];
|
||||
this.unit = '';
|
||||
this.multiSelect = false;
|
||||
this.selectedValues = [];
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -290,83 +224,10 @@ export default {
|
||||
color: #666d7f;
|
||||
line-height: 38rpx;
|
||||
}
|
||||
.btn-confirm {
|
||||
font-weight: 400;
|
||||
font-size: 32rpx;
|
||||
color: #256bfa;
|
||||
}
|
||||
}
|
||||
|
||||
.multi-select-list {
|
||||
padding: 20rpx 30rpx;
|
||||
max-height: calc(60vh - 120rpx);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
padding: 60rpx 0;
|
||||
color: #999999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.skill-tags-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.skill-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 12rpx 20rpx;
|
||||
border-radius: 20rpx;
|
||||
background-color: #f8f9fa;
|
||||
border: 2rpx solid #e8eaee;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 24rpx;
|
||||
color: #333333;
|
||||
white-space: nowrap;
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
background-color: #e9ecef;
|
||||
border-color: #d0d0d0;
|
||||
transform: translateY(-1rpx);
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
background-color: #dee2e6;
|
||||
}
|
||||
|
||||
.skill-tag-text {
|
||||
font-size: 24rpx;
|
||||
color: inherit;
|
||||
line-height: 1.2;
|
||||
.btn-confirm {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
&.skill-tag-active {
|
||||
background-color: #256bfa;
|
||||
border-color: #256bfa;
|
||||
color: #ffffff;
|
||||
box-shadow: 0 2rpx 8rpx rgba(37, 107, 250, 0.3);
|
||||
|
||||
.skill-tag-text {
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #1e5ce6;
|
||||
border-color: #1e5ce6;
|
||||
transform: translateY(-1rpx);
|
||||
box-shadow: 0 4rpx 12rpx rgba(37, 107, 250, 0.4);
|
||||
}
|
||||
font-size: 32rpx;
|
||||
color: #256bfa;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
>
|
||||
<image :src="currentItem == item.id ? item.selectedIconPath : item.iconPath"></image>
|
||||
</view>
|
||||
<view class="badge" v-if="item.badge && item.badge > 0">{{ item.badge }}</view>
|
||||
<view class="badge" v-if="item.badge">{{ item.badge }}</view>
|
||||
<view class="item-bottom" :class="[currentItem == item.id ? 'item-active' : '']">
|
||||
<text>{{ item.text }}</text>
|
||||
</view>
|
||||
@@ -19,7 +19,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, computed, watch, nextTick } from 'vue';
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { useReadMsg } from '@/stores/useReadMsg';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import useUserStore from '@/stores/useUserStore';
|
||||
@@ -36,60 +36,35 @@ const readMsg = useReadMsg();
|
||||
const { userInfo } = storeToRefs(useUserStore());
|
||||
const currentItem = ref(0);
|
||||
|
||||
// 监听用户类型变化,重新生成tabbar配置
|
||||
watch(() => userInfo.value?.isCompanyUser, (newIsCompanyUser, oldIsCompanyUser) => {
|
||||
console.log('midell-box用户类型变化监听:', { newIsCompanyUser, oldIsCompanyUser, userInfo: userInfo.value });
|
||||
if (newIsCompanyUser !== oldIsCompanyUser) {
|
||||
console.log('用户类型发生变化,重新生成tabbar:', newIsCompanyUser);
|
||||
// tabbarList是computed,会自动重新计算
|
||||
// 强制触发响应式更新
|
||||
nextTick(() => {
|
||||
console.log('tabbar配置已更新:', tabbarList.value);
|
||||
});
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
// 监听用户信息变化(包括登录状态)
|
||||
watch(() => userInfo.value, (newUserInfo, oldUserInfo) => {
|
||||
console.log('midell-box用户信息变化监听:', { newUserInfo, oldUserInfo });
|
||||
if (newUserInfo !== oldUserInfo) {
|
||||
console.log('用户信息发生变化,重新生成tabbar');
|
||||
// 强制触发响应式更新
|
||||
nextTick(() => {
|
||||
console.log('tabbar配置已更新:', tabbarList.value);
|
||||
});
|
||||
}
|
||||
}, { immediate: true, deep: true });
|
||||
|
||||
// 生成tabbar配置的函数
|
||||
const generateTabbarList = () => {
|
||||
// 根据用户类型生成不同的导航栏配置
|
||||
const tabbarList = computed(() => {
|
||||
const baseItems = [
|
||||
{
|
||||
id: 0,
|
||||
text: '职位2',
|
||||
text: '首页',
|
||||
path: '/pages/index/index',
|
||||
iconPath: '../../static/tabbar/calendar.png',
|
||||
selectedIconPath: '../../static/tabbar/calendared.png',
|
||||
centerItem: false,
|
||||
badge: readMsg.badges[0]?.count || 0,
|
||||
badge: readMsg.badges[0].count,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
text: 'AI+',
|
||||
text: '',
|
||||
path: '/pages/chat/chat',
|
||||
iconPath: '../../static/tabbar/logo3.png',
|
||||
selectedIconPath: '../../static/tabbar/logo3.png',
|
||||
centerItem: true,
|
||||
badge: readMsg.badges[2]?.count || 0,
|
||||
badge: readMsg.badges[2].count,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
text: '消息2',
|
||||
text: '消息',
|
||||
path: '/pages/msglog/msglog',
|
||||
iconPath: '../../static/tabbar/chat4.png',
|
||||
selectedIconPath: '../../static/tabbar/chat4ed.png',
|
||||
centerItem: false,
|
||||
badge: readMsg.badges[3]?.count || 0,
|
||||
badge: readMsg.badges[3].count,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
@@ -98,12 +73,12 @@ const generateTabbarList = () => {
|
||||
iconPath: '../../static/tabbar/mine.png',
|
||||
selectedIconPath: '../../static/tabbar/mined.png',
|
||||
centerItem: false,
|
||||
badge: readMsg.badges[4]?.count || 0,
|
||||
badge: readMsg.badges[4].count,
|
||||
},
|
||||
];
|
||||
|
||||
// 根据用户类型添加不同的导航项,未登录时默认为求职者
|
||||
const userType = userInfo.value?.isCompanyUser !== undefined ? userInfo.value.isCompanyUser : 1;
|
||||
// 根据用户类型添加不同的导航项
|
||||
const userType = userInfo.value?.userType || 0;
|
||||
|
||||
if (userType === 0) {
|
||||
// 企业用户:显示发布岗位,隐藏招聘会
|
||||
@@ -111,13 +86,13 @@ const generateTabbarList = () => {
|
||||
id: 1,
|
||||
text: '发布岗位',
|
||||
path: '/pages/job/publishJob',
|
||||
iconPath: '../../static/tabbar/post.png',
|
||||
selectedIconPath: '../../static/tabbar/posted.png',
|
||||
iconPath: '../../static/tabbar/publish-job.svg',
|
||||
selectedIconPath: '../../static/tabbar/publish-job-selected.svg',
|
||||
centerItem: false,
|
||||
badge: 0,
|
||||
});
|
||||
} else {
|
||||
// 求职者用户(包括未登录状态):显示招聘会
|
||||
// 普通用户、网格员、政府人员:显示招聘会
|
||||
baseItems.splice(1, 0, {
|
||||
id: 1,
|
||||
text: '招聘会',
|
||||
@@ -125,16 +100,11 @@ const generateTabbarList = () => {
|
||||
iconPath: '../../static/tabbar/post.png',
|
||||
selectedIconPath: '../../static/tabbar/posted.png',
|
||||
centerItem: false,
|
||||
badge: readMsg.badges[1]?.count || 0,
|
||||
badge: readMsg.badges[1].count,
|
||||
});
|
||||
}
|
||||
|
||||
return baseItems;
|
||||
};
|
||||
|
||||
// 根据用户类型生成不同的导航栏配置
|
||||
const tabbarList = computed(() => {
|
||||
return generateTabbarList();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
@@ -144,7 +114,7 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
const changeItem = (item) => {
|
||||
// 判断是否为 TabBar 页面
|
||||
// 判断是否为 tabBar 页面
|
||||
const tabBarPages = [
|
||||
'/pages/index/index',
|
||||
'/pages/careerfair/careerfair',
|
||||
@@ -154,12 +124,12 @@ const changeItem = (item) => {
|
||||
];
|
||||
|
||||
if (tabBarPages.includes(item.path)) {
|
||||
// TabBar 页面使用 redirectTo 避免页面栈溢出
|
||||
uni.redirectTo({
|
||||
// tabBar 页面使用 switchTab
|
||||
uni.switchTab({
|
||||
url: item.path,
|
||||
});
|
||||
} else {
|
||||
// 非 TabBar 页面使用 navigateTo
|
||||
// 非 tabBar 页面使用 navigateTo
|
||||
uni.navigateTo({
|
||||
url: item.path,
|
||||
});
|
||||
|
||||
@@ -102,7 +102,6 @@
|
||||
<script setup>
|
||||
import { ref, inject } from 'vue';
|
||||
import useUserStore from '@/stores/useUserStore';
|
||||
import { tabbarManager } from '@/utils/tabbarManager';
|
||||
|
||||
const { $api } = inject('globalFunction');
|
||||
const { loginSetToken } = useUserStore();
|
||||
@@ -163,33 +162,22 @@ const getPhoneNumber = (e) => {
|
||||
userType: userType.value
|
||||
}, 'post').then((resData) => {
|
||||
uni.hideLoading();
|
||||
console.log(resData, 'resume.idCard');
|
||||
|
||||
if (resData.token) {
|
||||
// 登录成功,存储token
|
||||
loginSetToken(resData.token).then((resume) => {
|
||||
// 更新用户类型到缓存
|
||||
if (resData.isCompanyUser !== undefined) {
|
||||
console.log(resData.isCompanyUser, 'resData.isCompanyUser');
|
||||
const userInfo = uni.getStorageSync('userInfo') || {};
|
||||
userInfo.isCompanyUser = Number(resData.isCompanyUser); // 0-企业用户,1-求职者
|
||||
uni.setStorageSync('userInfo', userInfo);
|
||||
}
|
||||
|
||||
$api.msg('登录成功');
|
||||
// 刷新tabbar以显示正确的用户类型
|
||||
tabbarManager.refreshTabBar();
|
||||
close();
|
||||
emit('success');
|
||||
|
||||
// 根据用户类型跳转到不同的信息补全页面
|
||||
if (!resume.jobTitleId) {
|
||||
console.log(resume, 'resume.idCard');
|
||||
if (userType.value === 1 && !resData.idCard) {
|
||||
if (!resume.data.jobTitleId) {
|
||||
if (userType.value === 1) {
|
||||
// 求职者跳转到个人信息补全页面
|
||||
uni.navigateTo({
|
||||
url: '/pages/complete-info/complete-info?step=1'
|
||||
});
|
||||
} else if (userType.value === 0 && !resData.idCard) {
|
||||
} else if (userType.value === 0) {
|
||||
// 招聘者跳转到企业信息补全页面
|
||||
uni.navigateTo({
|
||||
url: '/pages/complete-info/company-info'
|
||||
@@ -245,15 +233,6 @@ const wxLogin = () => {
|
||||
|
||||
if (resData.token) {
|
||||
loginSetToken(resData.token).then((resume) => {
|
||||
console.log(resData, 'resData.isCompanyUser');
|
||||
// 更新用户类型到缓存
|
||||
if (resData.isCompanyUser) {
|
||||
console.log(resData.isCompanyUser, 'resData.isCompanyUser');
|
||||
const userInfo = uni.getStorageSync('userInfo') || {};
|
||||
userInfo.isCompanyUser = Number(resData.isCompanyUser); // 0-企业用户,1-求职者
|
||||
uni.setStorageSync('userInfo', userInfo);
|
||||
}
|
||||
|
||||
$api.msg('登录成功');
|
||||
close();
|
||||
emit('success');
|
||||
@@ -306,16 +285,7 @@ const wxLogin = () => {
|
||||
}, 'post').then((resData) => {
|
||||
if (resData.token) {
|
||||
loginSetToken(resData.token).then((resume) => {
|
||||
// 更新用户类型到缓存
|
||||
if (resData.isCompanyUser !== undefined) {
|
||||
const userInfo = uni.getStorageSync('userInfo') || {};
|
||||
userInfo.isCompanyUser = resData.isCompanyUser ? 0 : 1; // 0-企业用户,1-求职者
|
||||
uni.setStorageSync('userInfo', userInfo);
|
||||
}
|
||||
|
||||
$api.msg('登录成功');
|
||||
// 刷新tabbar以显示正确的用户类型
|
||||
tabbarManager.refreshTabBar();
|
||||
close();
|
||||
emit('success');
|
||||
|
||||
@@ -360,16 +330,7 @@ const testLogin = () => {
|
||||
uni.hideLoading();
|
||||
|
||||
loginSetToken(resData.token).then((resume) => {
|
||||
// 更新用户类型到缓存
|
||||
if (resData.isCompanyUser !== undefined) {
|
||||
const userInfo = uni.getStorageSync('userInfo') || {};
|
||||
userInfo.isCompanyUser = resData.isCompanyUser ? 0 : 1; // 0-企业用户,1-求职者
|
||||
uni.setStorageSync('userInfo', userInfo);
|
||||
}
|
||||
|
||||
$api.msg('测试登录成功');
|
||||
// 刷新tabbar以显示正确的用户类型
|
||||
tabbarManager.refreshTabBar();
|
||||
close();
|
||||
emit('success');
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
export default {
|
||||
// baseUrl: 'http://39.98.44.136:8080', // 测试
|
||||
baseUrl: 'http://ks.zhaopinzao8dian.com/api/ks', // 测试
|
||||
|
||||
LCBaseUrl:'http://10.110.145.145:9100',//招聘、培训、帮扶
|
||||
LCBaseUrlInner:'http://10.110.145.145:10100',//内网端口
|
||||
// sseAI+
|
||||
// StreamBaseURl: 'http://39.98.44.136:8000',
|
||||
StreamBaseURl: 'https://qd.zhaopinzao8dian.com/ai',
|
||||
@@ -73,4 +70,4 @@ export default {
|
||||
desc: '融合海量岗位、智能简历匹配、竞争力分析,助你精准锁定理想职位!',
|
||||
imgUrl: 'https://qd.zhaopinzao8dian.com/file/csn/qd_shareLogo.jpg',
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
# 企业我的页面功能说明
|
||||
|
||||
## 功能概述
|
||||
|
||||
本功能为企业用户提供了专门的"我的"页面和企业信息展示页面,实现了根据用户类型显示不同内容的功能。
|
||||
|
||||
## 页面结构
|
||||
|
||||
### 1. 企业我的页面 (`pages/mine/company-mine.vue`)
|
||||
- **功能**: 企业用户的个人中心页面
|
||||
- **特点**:
|
||||
- 显示企业头像、名称和信息完整度
|
||||
- 包含服务专区(实名认证、通知与提醒)
|
||||
- 提供退出登录功能
|
||||
- 点击头像区域可跳转到企业信息页面
|
||||
|
||||
### 2. 企业信息展示页面 (`pages/mine/company-info.vue`)
|
||||
- **功能**: 显示详细的企业信息
|
||||
- **特点**:
|
||||
- 显示企业头像编辑功能
|
||||
- 展示完整的企业信息(名称、统一社会代码、注册地点等)
|
||||
- 支持编辑各项企业信息
|
||||
- 包含企业联系人和法人信息
|
||||
|
||||
### 3. 修改后的我的页面 (`pages/mine/mine.vue`)
|
||||
- **功能**: 根据用户类型显示不同的内容
|
||||
- **特点**:
|
||||
- 企业用户显示企业信息卡片
|
||||
- 求职者用户显示个人简历信息
|
||||
- 自动根据 `userInfo.isCompanyUser` 字段判断用户类型
|
||||
|
||||
## 用户类型判断
|
||||
|
||||
系统通过 `userInfo.isCompanyUser` 字段来判断用户类型:
|
||||
- `0` = 企业用户
|
||||
- `1` = 求职者
|
||||
- `2` = 网格员
|
||||
- `3` = 政府人员
|
||||
|
||||
## 页面跳转逻辑
|
||||
|
||||
### 从我的页面跳转
|
||||
- **企业用户**: 点击头像区域 → 跳转到企业信息页面 (`/pages/mine/company-info`)
|
||||
- **求职者用户**: 点击头像区域 → 跳转到简历页面 (`/packageA/pages/myResume/myResume`)
|
||||
|
||||
### 企业信息页面功能
|
||||
- 点击头像 → 编辑头像(调用相册选择图片)
|
||||
- 点击各项信息 → 跳转到对应的编辑页面(需要后续开发)
|
||||
|
||||
## 路由配置
|
||||
|
||||
新增的路由配置:
|
||||
```json
|
||||
{
|
||||
"path": "pages/mine/company-mine",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/mine/company-info",
|
||||
"style": {
|
||||
"navigationBarTitleText": "企业信息",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 测试页面
|
||||
|
||||
创建了测试页面 `pages/test/company-mine-test.vue` 用于测试功能:
|
||||
- 用户类型切换测试
|
||||
- 页面跳转测试
|
||||
- 用户信息显示
|
||||
|
||||
## 样式特点
|
||||
|
||||
### 企业信息卡片
|
||||
- 白色背景,圆角设计
|
||||
- 阴影效果,现代化UI
|
||||
- 头像圆形显示
|
||||
- 信息完整度显示
|
||||
|
||||
### 企业信息页面
|
||||
- 清晰的信息层级
|
||||
- 可点击的编辑区域
|
||||
- 统一的视觉风格
|
||||
|
||||
## 数据流
|
||||
|
||||
1. 用户登录时设置 `userInfo.isCompanyUser` 字段
|
||||
2. 我的页面根据此字段判断显示内容
|
||||
3. 企业用户点击头像跳转到企业信息页面
|
||||
4. 企业信息页面展示详细的企业数据
|
||||
|
||||
## 后续开发建议
|
||||
|
||||
1. **编辑功能**: 为每个信息项创建对应的编辑页面
|
||||
2. **数据接口**: 连接真实的企业信息API
|
||||
3. **头像上传**: 完善头像上传功能
|
||||
4. **表单验证**: 添加企业信息编辑的表单验证
|
||||
5. **权限控制**: 根据用户权限控制可编辑的字段
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 在测试页面切换用户类型为企业用户
|
||||
2. 访问我的页面,查看企业信息卡片
|
||||
3. 点击头像区域跳转到企业信息页面
|
||||
4. 在企业信息页面查看详细的企业信息
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 确保用户类型字段正确设置
|
||||
- 企业信息数据需要从后端API获取
|
||||
- 头像上传功能需要配置服务器接口
|
||||
- 编辑页面需要根据实际需求进行开发
|
||||
@@ -1,187 +0,0 @@
|
||||
# 企业搜索功能实现说明
|
||||
|
||||
## 功能概述
|
||||
|
||||
根据用户类型对发布岗位页面的"招聘公司"输入框进行不同的交互处理:
|
||||
|
||||
- **企业用户**:直接输入公司名称
|
||||
- **网格员**:点击输入框跳转到企业搜索页面,支持模糊查询
|
||||
|
||||
## 实现细节
|
||||
|
||||
### 1. 用户类型判断
|
||||
|
||||
通过 `userInfo.isCompanyUser` 字段判断用户类型:
|
||||
- `0`: 企业用户
|
||||
- `1`: 求职者
|
||||
- `2`: 网格员
|
||||
- `3`: 政府人员
|
||||
|
||||
### 2. 页面修改
|
||||
|
||||
#### 发布岗位页面 (`pages/job/publishJob.vue`)
|
||||
|
||||
**模板修改:**
|
||||
```vue
|
||||
<!-- 企业用户:直接输入 -->
|
||||
<input
|
||||
v-if="isCompanyUser"
|
||||
class="input"
|
||||
placeholder="请输入公司名称"
|
||||
v-model="formData.companyName"
|
||||
/>
|
||||
<!-- 网格员:点击跳转到搜索页面 -->
|
||||
<view
|
||||
v-else
|
||||
class="company-selector"
|
||||
@click="openCompanySearch"
|
||||
>
|
||||
<view class="selector-text" :class="{ 'placeholder': !formData.companyName }">
|
||||
{{ formData.companyName || '请选择企业' }}
|
||||
</view>
|
||||
<view class="selector-icon">
|
||||
<view class="arrow-icon">></view>
|
||||
</view>
|
||||
</view>
|
||||
```
|
||||
|
||||
**脚本修改:**
|
||||
- 添加用户类型判断逻辑
|
||||
- 添加打开企业搜索页面的方法
|
||||
- 添加页面显示时处理返回数据的逻辑
|
||||
|
||||
#### 企业搜索页面 (`pages/job/companySearch.vue`)
|
||||
|
||||
**功能特性:**
|
||||
- 搜索框支持实时输入
|
||||
- 防抖节流:500ms延迟执行搜索
|
||||
- 调用接口:`/app/company/likeList`,参数:`name`
|
||||
- 支持企业选择和数据回传
|
||||
- 空状态和加载状态处理
|
||||
|
||||
**核心代码:**
|
||||
```javascript
|
||||
// 防抖搜索
|
||||
const onSearchInput = () => {
|
||||
if (debounceTimer) {
|
||||
clearTimeout(debounceTimer);
|
||||
}
|
||||
|
||||
debounceTimer = setTimeout(() => {
|
||||
if (searchKeyword.value.trim()) {
|
||||
searchCompanies();
|
||||
} else {
|
||||
searchResults.value = [];
|
||||
}
|
||||
}, 500);
|
||||
};
|
||||
|
||||
// 搜索企业
|
||||
const searchCompanies = async () => {
|
||||
const response = await createRequest('/app/company/likeList', {
|
||||
name: searchKeyword.value.trim()
|
||||
}, 'GET', false);
|
||||
|
||||
if (response.code === 200) {
|
||||
searchResults.value = response.data || [];
|
||||
}
|
||||
};
|
||||
|
||||
// 选择企业
|
||||
const selectCompany = (company) => {
|
||||
uni.navigateBack({
|
||||
success: () => {
|
||||
getApp().globalData = getApp().globalData || {};
|
||||
getApp().globalData.selectedCompany = company;
|
||||
}
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
### 3. 数据传递
|
||||
|
||||
使用全局数据传递选中的企业信息:
|
||||
|
||||
**企业搜索页面:**
|
||||
```javascript
|
||||
// 选择企业后设置全局数据
|
||||
getApp().globalData.selectedCompany = company;
|
||||
```
|
||||
|
||||
**发布岗位页面:**
|
||||
```javascript
|
||||
// 页面显示时检查全局数据
|
||||
onShow(() => {
|
||||
const app = getApp();
|
||||
if (app.globalData && app.globalData.selectedCompany) {
|
||||
const selectedCompany = app.globalData.selectedCompany;
|
||||
formData.companyName = selectedCompany.name;
|
||||
formData.companyId = selectedCompany.id;
|
||||
|
||||
// 清除全局数据
|
||||
app.globalData.selectedCompany = null;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 4. 页面配置
|
||||
|
||||
在 `pages.json` 中添加了企业搜索页面配置:
|
||||
|
||||
```json
|
||||
{
|
||||
"path": "pages/job/companySearch",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择企业",
|
||||
"navigationStyle": "custom",
|
||||
"disableScroll": false,
|
||||
"enablePullDownRefresh": false,
|
||||
"backgroundColor": "#f5f5f5"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 测试页面
|
||||
|
||||
创建了测试页面 `pages/test/company-search-test.vue` 用于验证功能:
|
||||
- 用户类型切换
|
||||
- 功能说明展示
|
||||
- 直接跳转到发布岗位页面测试
|
||||
|
||||
## 使用说明
|
||||
|
||||
### 企业用户
|
||||
1. 进入发布岗位页面
|
||||
2. 招聘公司输入框为普通输入框
|
||||
3. 直接输入公司名称
|
||||
|
||||
### 网格员
|
||||
1. 进入发布岗位页面
|
||||
2. 点击"招聘公司"输入框
|
||||
3. 跳转到企业搜索页面
|
||||
4. 输入企业名称进行搜索(支持防抖)
|
||||
5. 选择企业后自动返回
|
||||
6. 企业名称显示在输入框中
|
||||
|
||||
## 技术特点
|
||||
|
||||
1. **防抖节流**:搜索输入500ms延迟,避免频繁请求
|
||||
2. **用户类型判断**:根据 `isCompanyUser` 字段动态显示不同交互
|
||||
3. **数据传递**:使用全局数据实现页面间数据传递
|
||||
4. **响应式设计**:支持不同屏幕尺寸
|
||||
5. **错误处理**:完善的错误提示和空状态处理
|
||||
|
||||
## 接口说明
|
||||
|
||||
**搜索企业接口:**
|
||||
- 地址:`/app/company/likeList`
|
||||
- 方法:`GET`
|
||||
- 参数:`name` (企业名称)
|
||||
- 返回:企业列表数据
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 确保用户类型字段 `isCompanyUser` 正确设置
|
||||
2. 搜索接口需要支持模糊查询
|
||||
3. 企业数据需要包含 `id` 和 `name` 字段
|
||||
4. 防抖时间可根据实际需求调整
|
||||
@@ -1,129 +0,0 @@
|
||||
# 自定义TabBar使用说明
|
||||
|
||||
## 功能概述
|
||||
|
||||
本项目实现了基于用户类型的动态自定义TabBar,支持根据用户登录状态和类型显示不同的导航项:
|
||||
|
||||
- **未登录状态**:默认显示求职者tabbar(职位 + 招聘会 + AI+ + 消息 + 我的)
|
||||
- **企业用户(userType=0)**:显示"发布岗位"导航
|
||||
- **求职者用户(userType=1,2,3)**:显示"招聘会"导航
|
||||
|
||||
## 实现方案
|
||||
|
||||
### 1. 微信小程序原生自定义TabBar
|
||||
|
||||
在 `custom-tab-bar/` 目录下创建了微信小程序原生自定义TabBar组件:
|
||||
|
||||
- `index.js` - 组件逻辑,根据用户类型动态生成TabBar配置
|
||||
- `index.wxml` - 模板文件
|
||||
- `index.wxss` - 样式文件
|
||||
- `index.json` - 组件配置文件
|
||||
|
||||
### 2. UniApp兼容的自定义TabBar组件
|
||||
|
||||
创建了 `components/CustomTabBar/CustomTabBar.vue` 组件,支持多端兼容:
|
||||
|
||||
- 支持微信小程序、H5、APP等多端
|
||||
- 响应式设计,根据用户类型动态显示
|
||||
- 支持消息徽章显示
|
||||
- 支持页面跳转逻辑
|
||||
|
||||
### 3. 配置修改
|
||||
|
||||
在 `pages.json` 中启用了自定义TabBar:
|
||||
|
||||
```json
|
||||
"tabBar": {
|
||||
"custom": true,
|
||||
// ... 其他配置
|
||||
}
|
||||
```
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 1. 在页面中引入自定义TabBar
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<view class="page">
|
||||
<!-- 页面内容 -->
|
||||
|
||||
<!-- 自定义tabbar -->
|
||||
<CustomTabBar :currentPage="0" />
|
||||
</view>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 2. 用户类型判断
|
||||
|
||||
组件会自动从 `useUserStore` 中获取用户信息,根据用户登录状态和 `userInfo.userType` 字段判断用户类型:
|
||||
|
||||
```javascript
|
||||
// 用户类型说明
|
||||
// 未登录: 默认显示求职者tabbar
|
||||
// 0: 企业用户 - 显示"发布岗位"
|
||||
// 1: 求职者 - 显示"招聘会"
|
||||
// 2: 网格员 - 显示"招聘会"
|
||||
// 3: 政府人员 - 显示"招聘会"
|
||||
```
|
||||
|
||||
### 3. 动态切换用户类型
|
||||
|
||||
当用户登录状态或类型发生变化时,TabBar会自动更新:
|
||||
|
||||
```javascript
|
||||
// 未登录状态:自动显示求职者tabbar
|
||||
// 登录后根据用户类型显示对应tabbar
|
||||
|
||||
// 切换用户类型
|
||||
userInfo.value.userType = 1; // 切换到求职者
|
||||
uni.setStorageSync('userInfo', userInfo.value);
|
||||
|
||||
// 登出时清除用户信息,自动回到未登录状态
|
||||
userStore.logOut(false);
|
||||
```
|
||||
|
||||
## 页面配置
|
||||
|
||||
### 已配置的页面
|
||||
|
||||
- `pages/index/index.vue` - 首页(currentPage: 0)
|
||||
- `pages/careerfair/careerfair.vue` - 招聘会页面(currentPage: 1)
|
||||
- `pages/chat/chat.vue` - AI+页面(currentPage: 2)
|
||||
- `pages/msglog/msglog.vue` - 消息页面(currentPage: 3)
|
||||
- `pages/mine/mine.vue` - 我的页面(currentPage: 4)
|
||||
|
||||
### 测试页面
|
||||
|
||||
- `pages/test/tabbar-test.vue` - TabBar功能测试页面
|
||||
|
||||
## 技术特点
|
||||
|
||||
1. **响应式设计**:根据用户类型动态显示不同的导航项
|
||||
2. **多端兼容**:支持微信小程序、H5、APP等平台
|
||||
3. **消息徽章**:支持显示未读消息数量
|
||||
4. **页面跳转**:智能判断tabBar页面和普通页面的跳转方式
|
||||
5. **用户类型监听**:实时监听用户类型变化并更新TabBar
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 确保在 `pages.json` 中设置了 `"custom": true`
|
||||
2. 每个页面的 `currentPage` 参数需要正确设置
|
||||
3. 用户类型存储在 `userInfo.userType` 字段中
|
||||
4. 组件会自动监听用户类型变化并更新显示
|
||||
|
||||
## 测试方法
|
||||
|
||||
1. 访问测试页面:`/pages/test/tabbar-test`
|
||||
2. 切换不同的用户类型
|
||||
3. 观察底部TabBar的变化
|
||||
4. 测试页面跳转功能
|
||||
|
||||
## 故障排除
|
||||
|
||||
如果TabBar不显示或显示异常:
|
||||
|
||||
1. 检查 `pages.json` 中的 `custom: true` 配置
|
||||
2. 确认用户信息是否正确存储
|
||||
3. 检查组件是否正确引入
|
||||
4. 查看控制台是否有错误信息
|
||||
92
main.js
92
main.js
@@ -1,21 +1,9 @@
|
||||
/*
|
||||
* @Date: 2025-10-23 14:48:48
|
||||
* @LastEditors: shirlwang
|
||||
* @LastEditTime: 2025-10-31 18:11:22
|
||||
*/
|
||||
import App from './App'
|
||||
import App from '@/App'
|
||||
import * as Pinia from 'pinia'
|
||||
import globalFunction from './common/globalFunction'
|
||||
import './lib/string-similarity.min.js'
|
||||
import similarityJobs from './utils/similarity_Job.js';
|
||||
import config from './config.js';
|
||||
// 导入主包中的request.js用于字典服务
|
||||
// 在uni-app小程序环境中,主包不能直接引用分包中的模块
|
||||
import { request, get, post, packageRcRequest, packageRcGet, packageRcPost } from './utils/request.js';
|
||||
|
||||
// 将request, get, post函数挂载到全局,方便使用
|
||||
// 挂载分包专用的请求函数(使用固定baseURL和token)
|
||||
|
||||
import globalFunction from '@/common/globalFunction'
|
||||
import '@/lib/string-similarity.min.js'
|
||||
import similarityJobs from '@/utils/similarity_Job.js';
|
||||
import config from '@/config.js';
|
||||
// 组件
|
||||
import AppLayout from './components/AppLayout/AppLayout.vue';
|
||||
import Empty from './components/empty/empty.vue';
|
||||
@@ -32,48 +20,14 @@ const directives = import.meta.glob('./directives/*.js', {
|
||||
eager: true
|
||||
});
|
||||
|
||||
import { createSSRApp } from 'vue'
|
||||
// 导入已安装的uni-ui组件
|
||||
import uniIcons from './uni_modules/uni-icons/components/uni-icons/uni-icons.vue'
|
||||
import uniPopup from './uni_modules/uni-popup/components/uni-popup/uni-popup.vue'
|
||||
import {
|
||||
createSSRApp,
|
||||
} from 'vue'
|
||||
|
||||
// const foldFeature = window.visualViewport && 'segments' in window.visualViewport
|
||||
// console.log('是否支持多段屏幕:', foldFeature)
|
||||
|
||||
// 全局组件
|
||||
// 字典缓存,避免重复请求
|
||||
const dictCache = new Map();
|
||||
|
||||
// 获取字典数据的方法
|
||||
async function getDict(dictType, forceRefresh = false) {
|
||||
// 检查缓存
|
||||
if (dictCache.has(dictType) && !forceRefresh) {
|
||||
return dictCache.get(dictType);
|
||||
}
|
||||
|
||||
try {
|
||||
// 使用packageRc/utils/request.js中的请求方法
|
||||
// 注意:这里使用的baseURL是 http://10.160.0.5:8907/
|
||||
const response = await request({
|
||||
url: `system/dict/data/type/${dictType}`,
|
||||
method: 'GET',
|
||||
load: true
|
||||
});
|
||||
|
||||
// 处理响应数据
|
||||
if (response.code === 200 && response.data) {
|
||||
// 缓存数据
|
||||
dictCache.set(dictType, response.data);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
return [];
|
||||
} catch (error) {
|
||||
console.error(`获取字典[${dictType}]失败:`, error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App)
|
||||
|
||||
@@ -85,16 +39,11 @@ export function createApp() {
|
||||
app.component('RenderJobs', RenderJobs)
|
||||
app.component('RenderCompanys', RenderCompanys)
|
||||
// app.component('tabbar-custom', Tabbar)
|
||||
|
||||
// 注册已安装的uni-ui组件
|
||||
app.component('uni-icons', uniIcons)
|
||||
app.component('uni-popup', uniPopup)
|
||||
// 注意:项目缺少表单相关组件,需要将模板中的uni-ui组件改为原生元素
|
||||
|
||||
for (const path in directives) {
|
||||
const directiveModule = directives[path];
|
||||
// 文件名作为指令名,./directives/fade.js => v-fade
|
||||
const name = path.match(/\/directives\/(.*)\.js$/)[1];
|
||||
const name = path.match(/\.\/directives\/(.*)\.js$/)[1];
|
||||
app.directive(name, directiveModule.default);
|
||||
}
|
||||
|
||||
@@ -105,30 +54,11 @@ export function createApp() {
|
||||
});
|
||||
app.provide('deviceInfo', globalFunction.getdeviceInfo());
|
||||
|
||||
// 先注册Pinia
|
||||
app.use(Pinia.createPinia());
|
||||
// 注册vuex
|
||||
app.use(storeRc);
|
||||
|
||||
// 注册其他插件
|
||||
app.use(SelectPopupPlugin);
|
||||
|
||||
// Vue 3 中挂载全局属性 - 字典获取方法
|
||||
app.config.globalProperties.$getDict = getDict;
|
||||
app.config.globalProperties.$store = storeRc;
|
||||
app.config.globalProperties.$getDictSelectOption = async (dictType, isDigital = false, forceRefresh = false) => {
|
||||
const dictData = await getDict(dictType, forceRefresh);
|
||||
return dictData.map(item => ({
|
||||
value: isDigital ? Number(item.dictValue || item.dictvalue) : (item.dictValue || item.dictvalue),
|
||||
label: item.dictLabel || item.dictlabel,
|
||||
...item
|
||||
}));
|
||||
};
|
||||
|
||||
app.use(Pinia.createPinia());
|
||||
|
||||
return {
|
||||
app,
|
||||
Pinia
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -50,7 +50,7 @@
|
||||
"quickapp" : {},
|
||||
/* 小程序特有相关 */
|
||||
"mp-weixin" : {
|
||||
"appid" : "wx9d1cbc11c8c40ba7",
|
||||
"appid" : "wxc63baa791b81a51a",
|
||||
"setting" : {
|
||||
"urlCheck" : false,
|
||||
"es6" : true,
|
||||
|
||||
20
node_modules/.package-lock.json
generated
vendored
20
node_modules/.package-lock.json
generated
vendored
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"name": "ks-app-employment-service",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"node_modules/jsbn": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
|
||||
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
|
||||
},
|
||||
"node_modules/sm-crypto": {
|
||||
"version": "0.3.13",
|
||||
"resolved": "https://registry.npmjs.org/sm-crypto/-/sm-crypto-0.3.13.tgz",
|
||||
"integrity": "sha512-ztNF+pZq6viCPMA1A6KKu3bgpkmYti5avykRHbcFIdSipFdkVmfUw2CnpM2kBJyppIalqvczLNM3wR8OQ0pT5w==",
|
||||
"dependencies": {
|
||||
"jsbn": "^1.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
node_modules/jsbn/.npmignore
generated
vendored
2
node_modules/jsbn/.npmignore
generated
vendored
@@ -1,2 +0,0 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
16
node_modules/jsbn/CHANGELOG.md
generated
vendored
16
node_modules/jsbn/CHANGELOG.md
generated
vendored
@@ -1,16 +0,0 @@
|
||||
# v1.1.0
|
||||
|
||||
- Allow for es6 "default import", e.g. `import BigInteger from 'jsbn'`.
|
||||
- Updated license file to read MIT
|
||||
|
||||
|
||||
# v1.0.0
|
||||
|
||||
- breaking change: `require('jsbn')` no longer returns `BigInteger`. Use `require('jsbn').BigInteger` instead.
|
||||
|
||||
|
||||
|
||||
# v0.1.1
|
||||
|
||||
- fixed backwards-incompatible change in v0.1.0 where `require('jsbn') != BigInteger`. This patch version allows for `var BigInteger = require('jsbn')` or `var BigInteger = require('jsbn').BigInteger`.
|
||||
|
||||
40
node_modules/jsbn/LICENSE
generated
vendored
40
node_modules/jsbn/LICENSE
generated
vendored
@@ -1,40 +0,0 @@
|
||||
Licensing
|
||||
---------
|
||||
|
||||
This software is covered under the following copyright:
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003-2005 Tom Wu
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
|
||||
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
|
||||
* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
|
||||
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
|
||||
* THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
|
||||
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* In addition, the following condition applies:
|
||||
*
|
||||
* All redistributions must retain an intact copy of this copyright notice
|
||||
* and disclaimer.
|
||||
*/
|
||||
|
||||
Address all questions regarding this license to:
|
||||
|
||||
Tom Wu
|
||||
tjw@cs.Stanford.EDU
|
||||
173
node_modules/jsbn/README.md
generated
vendored
173
node_modules/jsbn/README.md
generated
vendored
@@ -1,173 +0,0 @@
|
||||
# jsbn: javascript big number
|
||||
|
||||
[Tom Wu's Original Website](http://www-cs-students.stanford.edu/~tjw/jsbn/)
|
||||
|
||||
I felt compelled to put this on github and publish to npm. I haven't tested every other big integer library out there, but the few that I have tested in comparison to this one have not even come close in performance. I am aware of the `bi` module on npm, however it has been modified and I wanted to publish the original without modifications. This is jsbn and jsbn2 from Tom Wu's original website above, with the module pattern applied to prevent global leaks and to allow for use with node.js on the server side.
|
||||
|
||||
## usage
|
||||
|
||||
var BigInteger = require('jsbn').BigInteger;
|
||||
|
||||
var bi = new BigInteger('91823918239182398123');
|
||||
console.log(bi.bitLength()); // 67
|
||||
|
||||
|
||||
## API
|
||||
|
||||
### bi.toString()
|
||||
|
||||
returns the base-10 number as a string
|
||||
|
||||
### bi.negate()
|
||||
|
||||
returns a new BigInteger equal to the negation of `bi`
|
||||
|
||||
### bi.abs
|
||||
|
||||
returns new BI of absolute value
|
||||
|
||||
### bi.compareTo
|
||||
|
||||
|
||||
|
||||
### bi.bitLength
|
||||
|
||||
|
||||
|
||||
### bi.mod
|
||||
|
||||
|
||||
|
||||
### bi.modPowInt
|
||||
|
||||
|
||||
|
||||
### bi.clone
|
||||
|
||||
|
||||
|
||||
### bi.intValue
|
||||
|
||||
|
||||
|
||||
### bi.byteValue
|
||||
|
||||
|
||||
|
||||
### bi.shortValue
|
||||
|
||||
|
||||
|
||||
### bi.signum
|
||||
|
||||
|
||||
|
||||
### bi.toByteArray
|
||||
|
||||
|
||||
|
||||
### bi.equals
|
||||
|
||||
|
||||
|
||||
### bi.min
|
||||
|
||||
|
||||
|
||||
### bi.max
|
||||
|
||||
|
||||
|
||||
### bi.and
|
||||
|
||||
|
||||
|
||||
### bi.or
|
||||
|
||||
|
||||
|
||||
### bi.xor
|
||||
|
||||
|
||||
|
||||
### bi.andNot
|
||||
|
||||
|
||||
|
||||
### bi.not
|
||||
|
||||
|
||||
|
||||
### bi.shiftLeft
|
||||
|
||||
|
||||
|
||||
### bi.shiftRight
|
||||
|
||||
|
||||
|
||||
### bi.getLowestSetBit
|
||||
|
||||
|
||||
|
||||
### bi.bitCount
|
||||
|
||||
|
||||
|
||||
### bi.testBit
|
||||
|
||||
|
||||
|
||||
### bi.setBit
|
||||
|
||||
|
||||
|
||||
### bi.clearBit
|
||||
|
||||
|
||||
|
||||
### bi.flipBit
|
||||
|
||||
|
||||
|
||||
### bi.add
|
||||
|
||||
|
||||
|
||||
### bi.subtract
|
||||
|
||||
|
||||
|
||||
### bi.multiply
|
||||
|
||||
|
||||
|
||||
### bi.divide
|
||||
|
||||
|
||||
|
||||
### bi.remainder
|
||||
|
||||
|
||||
|
||||
### bi.divideAndRemainder
|
||||
|
||||
|
||||
|
||||
### bi.modPow
|
||||
|
||||
|
||||
|
||||
### bi.modInverse
|
||||
|
||||
|
||||
|
||||
### bi.pow
|
||||
|
||||
|
||||
|
||||
### bi.gcd
|
||||
|
||||
|
||||
|
||||
### bi.isProbablePrime
|
||||
11
node_modules/jsbn/example.html
generated
vendored
11
node_modules/jsbn/example.html
generated
vendored
@@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="index.js"></script>
|
||||
<script src="example.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
5
node_modules/jsbn/example.js
generated
vendored
5
node_modules/jsbn/example.js
generated
vendored
@@ -1,5 +0,0 @@
|
||||
(function () {
|
||||
var BigInteger = jsbn.BigInteger;
|
||||
var a = new BigInteger('91823918239182398123');
|
||||
console.log(a.bitLength());
|
||||
}());
|
||||
1361
node_modules/jsbn/index.js
generated
vendored
1361
node_modules/jsbn/index.js
generated
vendored
File diff suppressed because it is too large
Load Diff
21
node_modules/jsbn/package.json
generated
vendored
21
node_modules/jsbn/package.json
generated
vendored
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"name": "jsbn",
|
||||
"version": "1.1.0",
|
||||
"description": "The jsbn library is a fast, portable implementation of large-number math in pure JavaScript, enabling public-key crypto and other applications on desktop and mobile browsers.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "mocha test.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/andyperlitch/jsbn.git"
|
||||
},
|
||||
"keywords": [
|
||||
"biginteger",
|
||||
"bignumber",
|
||||
"big",
|
||||
"integer"
|
||||
],
|
||||
"author": "Tom Wu",
|
||||
"license": "MIT"
|
||||
}
|
||||
3
node_modules/jsbn/test/es6-import.js
generated
vendored
3
node_modules/jsbn/test/es6-import.js
generated
vendored
@@ -1,3 +0,0 @@
|
||||
import {BigInteger} from '../';
|
||||
|
||||
console.log(typeof BigInteger)
|
||||
3
node_modules/sm-crypto/.babelrc
generated
vendored
3
node_modules/sm-crypto/.babelrc
generated
vendored
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"presets": ["es2015"]
|
||||
}
|
||||
97
node_modules/sm-crypto/.eslintrc.js
generated
vendored
97
node_modules/sm-crypto/.eslintrc.js
generated
vendored
@@ -1,97 +0,0 @@
|
||||
module.exports = {
|
||||
'extends': [
|
||||
'airbnb-base',
|
||||
'plugin:promise/recommended'
|
||||
],
|
||||
'parserOptions': {
|
||||
'ecmaVersion': 9,
|
||||
'ecmaFeatures': {
|
||||
'jsx': false
|
||||
},
|
||||
'sourceType': 'module'
|
||||
},
|
||||
'env': {
|
||||
'es6': true,
|
||||
'node': true,
|
||||
'jest': true
|
||||
},
|
||||
'plugins': [
|
||||
'import',
|
||||
'node',
|
||||
'promise'
|
||||
],
|
||||
'rules': {
|
||||
'arrow-parens': 'off',
|
||||
'comma-dangle': [
|
||||
'error',
|
||||
'only-multiline'
|
||||
],
|
||||
'complexity': ['error', 20],
|
||||
'func-names': 'off',
|
||||
'global-require': 'off',
|
||||
'handle-callback-err': [
|
||||
'error',
|
||||
'^(err|error)$'
|
||||
],
|
||||
'import/no-unresolved': [
|
||||
'error',
|
||||
{
|
||||
'caseSensitive': true,
|
||||
'commonjs': true,
|
||||
'ignore': ['^[^.]']
|
||||
}
|
||||
],
|
||||
'import/prefer-default-export': 'off',
|
||||
'linebreak-style': 'off',
|
||||
'no-catch-shadow': 'error',
|
||||
'no-continue': 'off',
|
||||
'no-div-regex': 'warn',
|
||||
'no-else-return': 'off',
|
||||
'no-param-reassign': 'off',
|
||||
'no-plusplus': 'off',
|
||||
'no-shadow': 'off',
|
||||
'no-multi-assign': 'off',
|
||||
'no-underscore-dangle': 'off',
|
||||
'node/no-deprecated-api': 'error',
|
||||
'node/process-exit-as-throw': 'error',
|
||||
'object-curly-spacing': [
|
||||
'error',
|
||||
'never'
|
||||
],
|
||||
'operator-linebreak': [
|
||||
'error',
|
||||
'after',
|
||||
{
|
||||
'overrides': {
|
||||
':': 'before',
|
||||
'?': 'before'
|
||||
}
|
||||
}
|
||||
],
|
||||
'prefer-arrow-callback': 'off',
|
||||
'prefer-destructuring': 'off',
|
||||
'prefer-template': 'off',
|
||||
'quote-props': [
|
||||
1,
|
||||
'as-needed',
|
||||
{
|
||||
'unnecessary': true
|
||||
}
|
||||
],
|
||||
'semi': [
|
||||
'error',
|
||||
'never'
|
||||
],
|
||||
'max-len': 'off',
|
||||
'no-bitwise': 'off',
|
||||
'no-mixed-operators': 'off',
|
||||
},
|
||||
'globals': {
|
||||
'window': true,
|
||||
'document': true,
|
||||
'App': true,
|
||||
'Page': true,
|
||||
'Component': true,
|
||||
'Behavior': true
|
||||
}
|
||||
}
|
||||
82
node_modules/sm-crypto/CHANGELOG.md
generated
vendored
82
node_modules/sm-crypto/CHANGELOG.md
generated
vendored
@@ -1,82 +0,0 @@
|
||||
## 0.3.13
|
||||
|
||||
* 支持根据私钥获取公钥
|
||||
|
||||
## 0.3.12
|
||||
|
||||
* 优化 sm3 运行性能
|
||||
|
||||
## 0.3.11
|
||||
|
||||
* sm2 支持压缩公钥
|
||||
|
||||
## 0.3.10
|
||||
|
||||
* 支持 sm3 hmac 模式
|
||||
|
||||
|
||||
## 0.3.9
|
||||
|
||||
* 补充 sm4 解密时的 padding 判断
|
||||
|
||||
## 0.3.8
|
||||
|
||||
* sm2 解密时兼容密文可能是大写的情况
|
||||
|
||||
## 0.3.7
|
||||
|
||||
* 默认填充改为 pkcs#7,如传入 pkcs#5 也转到 pkcs#7 逻辑
|
||||
|
||||
## 0.3.6
|
||||
|
||||
* sm2 加解密支持二进制数据
|
||||
|
||||
## 0.3.5
|
||||
|
||||
* sm2.generateKeyPairHex 支持完整的 BigInteger 入参
|
||||
|
||||
## 0.3.4
|
||||
|
||||
* sm2 支持验证公钥接口
|
||||
* sm2 生成密钥时支持自定义随机数
|
||||
|
||||
## 0.3.3
|
||||
|
||||
* dist 输出改成 umd 模式
|
||||
|
||||
## 0.3.2
|
||||
|
||||
* 修复 sm2 在 userId 长度大于 31 时新旧版本验签不通过的问题
|
||||
## 0.3.0
|
||||
|
||||
* sm2、sm3 重构
|
||||
* sm4 支持 cbc 模式
|
||||
|
||||
## 0.2.7
|
||||
|
||||
* 优化 sm3 性能
|
||||
|
||||
## 0.2.5
|
||||
|
||||
* sm3 支持字节数组输入
|
||||
|
||||
## 0.2.4
|
||||
|
||||
* 修复 sm4 四字节字符输出编码
|
||||
|
||||
## 0.2.3
|
||||
|
||||
* sm3/sm4 支持输入四字节字符
|
||||
|
||||
## 0.2.2
|
||||
|
||||
* sm3 支持中文输入
|
||||
|
||||
## 0.2.1
|
||||
|
||||
* 修复 sm2 点 16 进制串可能不满 64 位的问题
|
||||
|
||||
## 0.2.0
|
||||
|
||||
* sm4 默认支持 pkcs#5 填充方式
|
||||
* sm4 支持输入输出为字符串
|
||||
7
node_modules/sm-crypto/LICENCE_MIT
generated
vendored
7
node_modules/sm-crypto/LICENCE_MIT
generated
vendored
@@ -1,7 +0,0 @@
|
||||
Copyright © 2018 june01
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
171
node_modules/sm-crypto/README.md
generated
vendored
171
node_modules/sm-crypto/README.md
generated
vendored
@@ -1,171 +0,0 @@
|
||||
# sm-crypto
|
||||
|
||||
国密算法sm2、sm3和sm4的js实现。
|
||||
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
npm install --save sm-crypto
|
||||
```
|
||||
|
||||
## sm2
|
||||
|
||||
### 获取密钥对
|
||||
|
||||
```js
|
||||
const sm2 = require('sm-crypto').sm2
|
||||
|
||||
let keypair = sm2.generateKeyPairHex()
|
||||
|
||||
publicKey = keypair.publicKey // 公钥
|
||||
privateKey = keypair.privateKey // 私钥
|
||||
|
||||
// 默认生成公钥 130 位太长,可以压缩公钥到 66 位
|
||||
const compressedPublicKey = sm2.compressPublicKeyHex(publicKey) // compressedPublicKey 和 publicKey 等价
|
||||
sm2.comparePublicKeyHex(publicKey, compressedPublicKey) // 判断公钥是否等价
|
||||
|
||||
// 自定义随机数,参数会直接透传给 jsbn 库的 BigInteger 构造器
|
||||
// 注意:开发者使用自定义随机数,需要自行确保传入的随机数符合密码学安全
|
||||
let keypair2 = sm2.generateKeyPairHex('123123123123123')
|
||||
let keypair3 = sm2.generateKeyPairHex(256, SecureRandom)
|
||||
|
||||
let verifyResult = sm2.verifyPublicKey(publicKey) // 验证公钥
|
||||
verifyResult = sm2.verifyPublicKey(compressedPublicKey) // 验证公钥
|
||||
```
|
||||
|
||||
### 加密解密
|
||||
|
||||
```js
|
||||
const sm2 = require('sm-crypto').sm2
|
||||
const cipherMode = 1 // 1 - C1C3C2,0 - C1C2C3,默认为1
|
||||
|
||||
let encryptData = sm2.doEncrypt(msgString, publicKey, cipherMode) // 加密结果
|
||||
let decryptData = sm2.doDecrypt(encryptData, privateKey, cipherMode) // 解密结果
|
||||
|
||||
encryptData = sm2.doEncrypt(msgArray, publicKey, cipherMode) // 加密结果,输入数组
|
||||
decryptData = sm2.doDecrypt(encryptData, privateKey, cipherMode, {output: 'array'}) // 解密结果,输出数组
|
||||
```
|
||||
|
||||
> ps:密文会在解密时自动补充 `04`,如遇到其他工具补充的 `04` 需手动去除再传入。
|
||||
|
||||
### 签名验签
|
||||
|
||||
> ps:理论上来说,只做纯签名是最快的。
|
||||
|
||||
```js
|
||||
const sm2 = require('sm-crypto').sm2
|
||||
|
||||
// 纯签名 + 生成椭圆曲线点
|
||||
let sigValueHex = sm2.doSignature(msg, privateKey) // 签名
|
||||
let verifyResult = sm2.doVerifySignature(msg, sigValueHex, publicKey) // 验签结果
|
||||
|
||||
// 纯签名
|
||||
let sigValueHex2 = sm2.doSignature(msg, privateKey, {
|
||||
pointPool: [sm2.getPoint(), sm2.getPoint(), sm2.getPoint(), sm2.getPoint()], // 传入事先已生成好的椭圆曲线点,可加快签名速度
|
||||
}) // 签名
|
||||
let verifyResult2 = sm2.doVerifySignature(msg, sigValueHex2, publicKey) // 验签结果
|
||||
|
||||
// 纯签名 + 生成椭圆曲线点 + der编解码
|
||||
let sigValueHex3 = sm2.doSignature(msg, privateKey, {
|
||||
der: true,
|
||||
}) // 签名
|
||||
let verifyResult3 = sm2.doVerifySignature(msg, sigValueHex3, publicKey, {
|
||||
der: true,
|
||||
}) // 验签结果
|
||||
|
||||
// 纯签名 + 生成椭圆曲线点 + sm3杂凑
|
||||
let sigValueHex4 = sm2.doSignature(msg, privateKey, {
|
||||
hash: true,
|
||||
}) // 签名
|
||||
let verifyResult4 = sm2.doVerifySignature(msg, sigValueHex4, publicKey, {
|
||||
hash: true,
|
||||
}) // 验签结果
|
||||
|
||||
// 纯签名 + 生成椭圆曲线点 + sm3杂凑(不做公钥推导)
|
||||
let sigValueHex5 = sm2.doSignature(msg, privateKey, {
|
||||
hash: true,
|
||||
publicKey, // 传入公钥的话,可以去掉sm3杂凑中推导公钥的过程,速度会比纯签名 + 生成椭圆曲线点 + sm3杂凑快
|
||||
})
|
||||
let verifyResult5 = sm2.doVerifySignature(msg, sigValueHex5, publicKey, {
|
||||
hash: true,
|
||||
publicKey,
|
||||
})
|
||||
|
||||
// 纯签名 + 生成椭圆曲线点 + sm3杂凑 + 不做公钥推 + 添加 userId(长度小于 8192)
|
||||
// 默认 userId 值为 1234567812345678
|
||||
let sigValueHex6 = sm2.doSignature(msgString, privateKey, {
|
||||
hash: true,
|
||||
publicKey,
|
||||
userId: 'testUserId',
|
||||
})
|
||||
let verifyResult6 = sm2.doVerifySignature(msgString, sigValueHex6, publicKey, {
|
||||
hash: true,
|
||||
userId: 'testUserId',
|
||||
})
|
||||
```
|
||||
|
||||
### 获取椭圆曲线点
|
||||
|
||||
```js
|
||||
const sm2 = require('sm-crypto').sm2
|
||||
|
||||
let point = sm2.getPoint() // 获取一个椭圆曲线点,可在sm2签名时传入
|
||||
```
|
||||
|
||||
### 根据私钥获取公钥
|
||||
|
||||
```js
|
||||
const sm2 = require('sm-crypto).sm2
|
||||
|
||||
let publicKey = sm2.getPublicKeyFromPrivateKey(privateKey)
|
||||
```
|
||||
|
||||
## sm3
|
||||
|
||||
```js
|
||||
const sm3 = require('sm-crypto').sm3
|
||||
|
||||
let hashData = sm3('abc') // 杂凑
|
||||
|
||||
// hmac
|
||||
hashData = sm3('abc', {
|
||||
key: 'daac25c1512fe50f79b0e4526b93f5c0e1460cef40b6dd44af13caec62e8c60e0d885f3c6d6fb51e530889e6fd4ac743a6d332e68a0f2a3923f42585dceb93e9', // 要求为 16 进制串或字节数组
|
||||
})
|
||||
```
|
||||
|
||||
## sm4
|
||||
|
||||
### 加密
|
||||
|
||||
```js
|
||||
const sm4 = require('sm-crypto').sm4
|
||||
const msg = 'hello world! 我是 juneandgreen.' // 可以为 utf8 串或字节数组
|
||||
const key = '0123456789abcdeffedcba9876543210' // 可以为 16 进制串或字节数组,要求为 128 比特
|
||||
|
||||
let encryptData = sm4.encrypt(msg, key) // 加密,默认输出 16 进制字符串,默认使用 pkcs#7 填充(传 pkcs#5 也会走 pkcs#7 填充)
|
||||
let encryptData = sm4.encrypt(msg, key, {padding: 'none'}) // 加密,不使用 padding
|
||||
let encryptData = sm4.encrypt(msg, key, {padding: 'none', output: 'array'}) // 加密,不使用 padding,输出为字节数组
|
||||
let encryptData = sm4.encrypt(msg, key, {mode: 'cbc', iv: 'fedcba98765432100123456789abcdef'}) // 加密,cbc 模式
|
||||
```
|
||||
|
||||
### 解密
|
||||
|
||||
```js
|
||||
const sm4 = require('sm-crypto').sm4
|
||||
const encryptData = '0e395deb10f6e8a17e17823e1fd9bd98a1bff1df508b5b8a1efb79ec633d1bb129432ac1b74972dbe97bab04f024e89c' // 可以为 16 进制串或字节数组
|
||||
const key = '0123456789abcdeffedcba9876543210' // 可以为 16 进制串或字节数组,要求为 128 比特
|
||||
|
||||
let decryptData = sm4.decrypt(encryptData, key) // 解密,默认输出 utf8 字符串,默认使用 pkcs#7 填充(传 pkcs#5 也会走 pkcs#7 填充)
|
||||
let decryptData = sm4.decrypt(encryptData, key, {padding: 'none'}) // 解密,不使用 padding
|
||||
let decryptData = sm4.decrypt(encryptData, key, {padding: 'none', output: 'array'}) // 解密,不使用 padding,输出为字节数组
|
||||
let decryptData = sm4.decrypt(encryptData, key, {mode: 'cbc', iv: 'fedcba98765432100123456789abcdef'}) // 解密,cbc 模式
|
||||
```
|
||||
|
||||
## 其他实现
|
||||
|
||||
* 小程序移植版:[https://github.com/wechat-miniprogram/sm-crypto](https://github.com/wechat-miniprogram/sm-crypto)
|
||||
* java 实现(感谢 @antherd 提供):[https://github.com/antherd/sm-crypto](https://github.com/antherd/sm-crypto)
|
||||
* dart 实现(感谢 @luckykellan 提供):[https://github.com/luckykellan/dart_sm](https://github.com/luckykellan/dart_sm)
|
||||
## 协议
|
||||
|
||||
MIT
|
||||
1
node_modules/sm-crypto/dist/sm2.js
generated
vendored
1
node_modules/sm-crypto/dist/sm2.js
generated
vendored
File diff suppressed because one or more lines are too long
1
node_modules/sm-crypto/dist/sm3.js
generated
vendored
1
node_modules/sm-crypto/dist/sm3.js
generated
vendored
@@ -1 +0,0 @@
|
||||
!function(r,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.sm3=n():r.sm3=n()}("undefined"!=typeof self?self:this,function(){return function(r){function n(e){if(t[e])return t[e].exports;var o=t[e]={i:e,l:!1,exports:{}};return r[e].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var t={};return n.m=r,n.c=t,n.d=function(r,t,e){n.o(r,t)||Object.defineProperty(r,t,{configurable:!1,enumerable:!0,get:e})},n.n=function(r){var t=r&&r.__esModule?function(){return r.default}:function(){return r};return n.d(t,"a",t),t},n.o=function(r,n){return Object.prototype.hasOwnProperty.call(r,n)},n.p="",n(n.s=6)}({1:function(r,n,t){"use strict";function e(r){if(Array.isArray(r)){for(var n=0,t=Array(r.length);n<r.length;n++)t[n]=r[n];return t}return Array.from(r)}function o(r,n){var t=31&n;return r<<t|r>>>32-t}function u(r,n){for(var t=[],e=r.length-1;e>=0;e--)t[e]=255&(r[e]^n[e]);return t}function i(r){return r^o(r,9)^o(r,17)}function f(r){return r^o(r,15)^o(r,23)}function a(r){var n=8*r.length,t=n%512;t=t>=448?512-t%448-1:448-t-1;for(var u=new Array((t-7)/8),a=new Array(8),s=0,p=u.length;s<p;s++)u[s]=0;for(var h=0,v=a.length;h<v;h++)a[h]=0;n=n.toString(2);for(var y=7;y>=0;y--)if(n.length>8){var g=n.length-8;a[y]=parseInt(n.substr(g),2),n=n.substr(0,g)}else n.length>0&&(a[y]=parseInt(n,2),n="");for(var d=new Uint8Array([].concat(e(r),[128],u,a)),w=new DataView(d.buffer,0),m=d.length/64,A=new Uint32Array([1937774191,1226093241,388252375,3666478592,2842636476,372324522,3817729613,2969243214]),b=0;b<m;b++){c.fill(0),l.fill(0);for(var x=16*b,j=0;j<16;j++)c[j]=w.getUint32(4*(x+j),!1);for(var U=16;U<68;U++)c[U]=f(c[U-16]^c[U-9]^o(c[U-3],15))^o(c[U-13],7)^c[U-6];for(var E=0;E<64;E++)l[E]=c[E]^c[E+4];for(var I=A[0],O=A[1],P=A[2],k=A[3],S=A[4],_=A[5],D=A[6],M=A[7],V=void 0,q=void 0,z=void 0,B=void 0,C=void 0,F=0;F<64;F++)C=F>=0&&F<=15?2043430169:2055708042,V=o(o(I,12)+S+o(C,F),7),q=V^o(I,12),z=(F>=0&&F<=15?I^O^P:I&O|I&P|O&P)+k+q+l[F],B=(F>=0&&F<=15?S^_^D:S&_|~S&D)+M+V+c[F],k=P,P=o(O,9),O=I,I=z,M=D,D=o(_,19),_=S,S=i(B);A[0]^=I,A[1]^=O,A[2]^=P,A[3]^=k,A[4]^=S,A[5]^=_,A[6]^=D,A[7]^=M}for(var G=[],H=0,J=A.length;H<J;H++){var K=A[H];G.push((4278190080&K)>>>24,(16711680&K)>>>16,(65280&K)>>>8,255&K)}return G}function s(r,n){for(n.length>p&&(n=a(n));n.length<p;)n.push(0);var t=u(n,h),o=u(n,v),i=a([].concat(e(t),e(r)));return a([].concat(e(o),e(i)))}for(var c=new Uint32Array(68),l=new Uint32Array(64),p=64,h=new Uint8Array(p),v=new Uint8Array(p),y=0;y<p;y++)h[y]=54,v[y]=92;r.exports={sm3:a,hmac:s}},6:function(r,n,t){"use strict";function e(r,n){return r.length>=n?r:new Array(n-r.length+1).join("0")+r}function o(r){return r.map(function(r){return r=r.toString(16),1===r.length?"0"+r:r}).join("")}function u(r){var n=[],t=r.length;t%2!=0&&(r=e(r,t+1)),t=r.length;for(var o=0;o<t;o+=2)n.push(parseInt(r.substr(o,2),16));return n}function i(r){for(var n=[],t=0,e=r.length;t<e;t++){var o=r.codePointAt(t);if(o<=127)n.push(o);else if(o<=2047)n.push(192|o>>>6),n.push(128|63&o);else if(o<=55295||o>=57344&&o<=65535)n.push(224|o>>>12),n.push(128|o>>>6&63),n.push(128|63&o);else{if(!(o>=65536&&o<=1114111))throw n.push(o),new Error("input is not supported");t++,n.push(240|o>>>18&28),n.push(128|o>>>12&63),n.push(128|o>>>6&63),n.push(128|63&o)}}return n}var f=t(1),a=f.sm3,s=f.hmac;r.exports=function(r,n){if(r="string"==typeof r?i(r):Array.prototype.slice.call(r),n){if("hmac"!==(n.mode||"hmac"))throw new Error("invalid mode");var t=n.key;if(!t)throw new Error("invalid key");return t="string"==typeof t?u(t):Array.prototype.slice.call(t),o(s(r,t))}return o(a(r))}}})});
|
||||
1
node_modules/sm-crypto/dist/sm4.js
generated
vendored
1
node_modules/sm-crypto/dist/sm4.js
generated
vendored
File diff suppressed because one or more lines are too long
42
node_modules/sm-crypto/package.json
generated
vendored
42
node_modules/sm-crypto/package.json
generated
vendored
@@ -1,42 +0,0 @@
|
||||
{
|
||||
"name": "sm-crypto",
|
||||
"version": "0.3.13",
|
||||
"description": "sm-crypto",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"prepublish": "npm run build",
|
||||
"test": "jest ./test/*",
|
||||
"lint": "eslint \"src/**/*.js\" --fix",
|
||||
"build": "npm run lint && webpack"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/JuneAndGreen/sm-crypto.git"
|
||||
},
|
||||
"keywords": [
|
||||
"sm",
|
||||
"js",
|
||||
"crypto"
|
||||
],
|
||||
"jest": {
|
||||
"testEnvironment": "jsdom",
|
||||
"testURL": "https://jest.test"
|
||||
},
|
||||
"author": "june_01",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jsbn": "^1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-loader": "^7.1.2",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"jest": "^22.1.4",
|
||||
"webpack": "^3.10.0",
|
||||
"eslint": "^5.3.0",
|
||||
"eslint-config-airbnb-base": "13.1.0",
|
||||
"eslint-plugin-import": "^2.14.0",
|
||||
"eslint-plugin-node": "^7.0.1",
|
||||
"eslint-plugin-promise": "^3.8.0"
|
||||
}
|
||||
}
|
||||
5
node_modules/sm-crypto/src/index.js
generated
vendored
5
node_modules/sm-crypto/src/index.js
generated
vendored
@@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
sm2: require('./sm2/index'),
|
||||
sm3: require('./sm3/index'),
|
||||
sm4: require('./sm4/index'),
|
||||
}
|
||||
161
node_modules/sm-crypto/src/sm2/asn1.js
generated
vendored
161
node_modules/sm-crypto/src/sm2/asn1.js
generated
vendored
@@ -1,161 +0,0 @@
|
||||
/* eslint-disable class-methods-use-this */
|
||||
const {BigInteger} = require('jsbn')
|
||||
|
||||
function bigintToValue(bigint) {
|
||||
let h = bigint.toString(16)
|
||||
if (h[0] !== '-') {
|
||||
// 正数
|
||||
if (h.length % 2 === 1) h = '0' + h // 补齐到整字节
|
||||
else if (!h.match(/^[0-7]/)) h = '00' + h // 非0开头,则补一个全0字节
|
||||
} else {
|
||||
// 负数
|
||||
h = h.substr(1)
|
||||
|
||||
let len = h.length
|
||||
if (len % 2 === 1) len += 1 // 补齐到整字节
|
||||
else if (!h.match(/^[0-7]/)) len += 2 // 非0开头,则补一个全0字节
|
||||
|
||||
let mask = ''
|
||||
for (let i = 0; i < len; i++) mask += 'f'
|
||||
mask = new BigInteger(mask, 16)
|
||||
|
||||
// 对绝对值取反,加1
|
||||
h = mask.xor(bigint).add(BigInteger.ONE)
|
||||
h = h.toString(16).replace(/^-/, '')
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
class ASN1Object {
|
||||
constructor() {
|
||||
this.tlv = null
|
||||
this.t = '00'
|
||||
this.l = '00'
|
||||
this.v = ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 der 编码比特流16进制串
|
||||
*/
|
||||
getEncodedHex() {
|
||||
if (!this.tlv) {
|
||||
this.v = this.getValue()
|
||||
this.l = this.getLength()
|
||||
this.tlv = this.t + this.l + this.v
|
||||
}
|
||||
return this.tlv
|
||||
}
|
||||
|
||||
getLength() {
|
||||
const n = this.v.length / 2 // 字节数
|
||||
let nHex = n.toString(16)
|
||||
if (nHex.length % 2 === 1) nHex = '0' + nHex // 补齐到整字节
|
||||
|
||||
if (n < 128) {
|
||||
// 短格式,以 0 开头
|
||||
return nHex
|
||||
} else {
|
||||
// 长格式,以 1 开头
|
||||
const head = 128 + nHex.length / 2 // 1(1位) + 真正的长度占用字节数(7位) + 真正的长度
|
||||
return head.toString(16) + nHex
|
||||
}
|
||||
}
|
||||
|
||||
getValue() {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
class DERInteger extends ASN1Object {
|
||||
constructor(bigint) {
|
||||
super()
|
||||
|
||||
this.t = '02' // 整型标签说明
|
||||
if (bigint) this.v = bigintToValue(bigint)
|
||||
}
|
||||
|
||||
getValue() {
|
||||
return this.v
|
||||
}
|
||||
}
|
||||
|
||||
class DERSequence extends ASN1Object {
|
||||
constructor(asn1Array) {
|
||||
super()
|
||||
|
||||
this.t = '30' // 序列标签说明
|
||||
this.asn1Array = asn1Array
|
||||
}
|
||||
|
||||
getValue() {
|
||||
this.v = this.asn1Array.map(asn1Object => asn1Object.getEncodedHex()).join('')
|
||||
return this.v
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 l 占用字节数
|
||||
*/
|
||||
function getLenOfL(str, start) {
|
||||
if (+str[start + 2] < 8) return 1 // l 以0开头,则表示短格式,只占一个字节
|
||||
return +str.substr(start + 2, 2) & 0x7f + 1 // 长格式,取第一个字节后7位作为长度真正占用字节数,再加上本身
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 l
|
||||
*/
|
||||
function getL(str, start) {
|
||||
// 获取 l
|
||||
const len = getLenOfL(str, start)
|
||||
const l = str.substr(start + 2, len * 2)
|
||||
|
||||
if (!l) return -1
|
||||
const bigint = +l[0] < 8 ? new BigInteger(l, 16) : new BigInteger(l.substr(2), 16)
|
||||
|
||||
return bigint.intValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 v 的位置
|
||||
*/
|
||||
function getStartOfV(str, start) {
|
||||
const len = getLenOfL(str, start)
|
||||
return start + (len + 1) * 2
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* ASN.1 der 编码,针对 sm2 签名
|
||||
*/
|
||||
encodeDer(r, s) {
|
||||
const derR = new DERInteger(r)
|
||||
const derS = new DERInteger(s)
|
||||
const derSeq = new DERSequence([derR, derS])
|
||||
|
||||
return derSeq.getEncodedHex()
|
||||
},
|
||||
|
||||
/**
|
||||
* 解析 ASN.1 der,针对 sm2 验签
|
||||
*/
|
||||
decodeDer(input) {
|
||||
// 结构:
|
||||
// input = | tSeq | lSeq | vSeq |
|
||||
// vSeq = | tR | lR | vR | tS | lS | vS |
|
||||
const start = getStartOfV(input, 0)
|
||||
|
||||
const vIndexR = getStartOfV(input, start)
|
||||
const lR = getL(input, start)
|
||||
const vR = input.substr(vIndexR, lR * 2)
|
||||
|
||||
const nextStart = vIndexR + vR.length
|
||||
const vIndexS = getStartOfV(input, nextStart)
|
||||
const lS = getL(input, nextStart)
|
||||
const vS = input.substr(vIndexS, lS * 2)
|
||||
|
||||
const r = new BigInteger(vR, 16)
|
||||
const s = new BigInteger(vS, 16)
|
||||
|
||||
return {r, s}
|
||||
}
|
||||
}
|
||||
332
node_modules/sm-crypto/src/sm2/ec.js
generated
vendored
332
node_modules/sm-crypto/src/sm2/ec.js
generated
vendored
@@ -1,332 +0,0 @@
|
||||
/* eslint-disable no-case-declarations, max-len */
|
||||
const {BigInteger} = require('jsbn')
|
||||
|
||||
/**
|
||||
* thanks for Tom Wu : http://www-cs-students.stanford.edu/~tjw/jsbn/
|
||||
*
|
||||
* Basic Javascript Elliptic Curve implementation
|
||||
* Ported loosely from BouncyCastle's Java EC code
|
||||
* Only Fp curves implemented for now
|
||||
*/
|
||||
|
||||
const TWO = new BigInteger('2')
|
||||
const THREE = new BigInteger('3')
|
||||
|
||||
/**
|
||||
* 椭圆曲线域元素
|
||||
*/
|
||||
class ECFieldElementFp {
|
||||
constructor(q, x) {
|
||||
this.x = x
|
||||
this.q = q
|
||||
// TODO if (x.compareTo(q) >= 0) error
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断相等
|
||||
*/
|
||||
equals(other) {
|
||||
if (other === this) return true
|
||||
return (this.q.equals(other.q) && this.x.equals(other.x))
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回具体数值
|
||||
*/
|
||||
toBigInteger() {
|
||||
return this.x
|
||||
}
|
||||
|
||||
/**
|
||||
* 取反
|
||||
*/
|
||||
negate() {
|
||||
return new ECFieldElementFp(this.q, this.x.negate().mod(this.q))
|
||||
}
|
||||
|
||||
/**
|
||||
* 相加
|
||||
*/
|
||||
add(b) {
|
||||
return new ECFieldElementFp(this.q, this.x.add(b.toBigInteger()).mod(this.q))
|
||||
}
|
||||
|
||||
/**
|
||||
* 相减
|
||||
*/
|
||||
subtract(b) {
|
||||
return new ECFieldElementFp(this.q, this.x.subtract(b.toBigInteger()).mod(this.q))
|
||||
}
|
||||
|
||||
/**
|
||||
* 相乘
|
||||
*/
|
||||
multiply(b) {
|
||||
return new ECFieldElementFp(this.q, this.x.multiply(b.toBigInteger()).mod(this.q))
|
||||
}
|
||||
|
||||
/**
|
||||
* 相除
|
||||
*/
|
||||
divide(b) {
|
||||
return new ECFieldElementFp(this.q, this.x.multiply(b.toBigInteger().modInverse(this.q)).mod(this.q))
|
||||
}
|
||||
|
||||
/**
|
||||
* 平方
|
||||
*/
|
||||
square() {
|
||||
return new ECFieldElementFp(this.q, this.x.square().mod(this.q))
|
||||
}
|
||||
}
|
||||
|
||||
class ECPointFp {
|
||||
constructor(curve, x, y, z) {
|
||||
this.curve = curve
|
||||
this.x = x
|
||||
this.y = y
|
||||
// 标准射影坐标系:zinv == null 或 z * zinv == 1
|
||||
this.z = z == null ? BigInteger.ONE : z
|
||||
this.zinv = null
|
||||
// TODO: compression flag
|
||||
}
|
||||
|
||||
getX() {
|
||||
if (this.zinv === null) this.zinv = this.z.modInverse(this.curve.q)
|
||||
|
||||
return this.curve.fromBigInteger(this.x.toBigInteger().multiply(this.zinv).mod(this.curve.q))
|
||||
}
|
||||
|
||||
getY() {
|
||||
if (this.zinv === null) this.zinv = this.z.modInverse(this.curve.q)
|
||||
|
||||
return this.curve.fromBigInteger(this.y.toBigInteger().multiply(this.zinv).mod(this.curve.q))
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断相等
|
||||
*/
|
||||
equals(other) {
|
||||
if (other === this) return true
|
||||
if (this.isInfinity()) return other.isInfinity()
|
||||
if (other.isInfinity()) return this.isInfinity()
|
||||
|
||||
// u = y2 * z1 - y1 * z2
|
||||
const u = other.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(other.z)).mod(this.curve.q)
|
||||
if (!u.equals(BigInteger.ZERO)) return false
|
||||
|
||||
// v = x2 * z1 - x1 * z2
|
||||
const v = other.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(other.z)).mod(this.curve.q)
|
||||
return v.equals(BigInteger.ZERO)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是无穷远点
|
||||
*/
|
||||
isInfinity() {
|
||||
if ((this.x === null) && (this.y === null)) return true
|
||||
return this.z.equals(BigInteger.ZERO) && !this.y.toBigInteger().equals(BigInteger.ZERO)
|
||||
}
|
||||
|
||||
/**
|
||||
* 取反,x 轴对称点
|
||||
*/
|
||||
negate() {
|
||||
return new ECPointFp(this.curve, this.x, this.y.negate(), this.z)
|
||||
}
|
||||
|
||||
/**
|
||||
* 相加
|
||||
*
|
||||
* 标准射影坐标系:
|
||||
*
|
||||
* λ1 = x1 * z2
|
||||
* λ2 = x2 * z1
|
||||
* λ3 = λ1 − λ2
|
||||
* λ4 = y1 * z2
|
||||
* λ5 = y2 * z1
|
||||
* λ6 = λ4 − λ5
|
||||
* λ7 = λ1 + λ2
|
||||
* λ8 = z1 * z2
|
||||
* λ9 = λ3^2
|
||||
* λ10 = λ3 * λ9
|
||||
* λ11 = λ8 * λ6^2 − λ7 * λ9
|
||||
* x3 = λ3 * λ11
|
||||
* y3 = λ6 * (λ9 * λ1 − λ11) − λ4 * λ10
|
||||
* z3 = λ10 * λ8
|
||||
*/
|
||||
add(b) {
|
||||
if (this.isInfinity()) return b
|
||||
if (b.isInfinity()) return this
|
||||
|
||||
const x1 = this.x.toBigInteger()
|
||||
const y1 = this.y.toBigInteger()
|
||||
const z1 = this.z
|
||||
const x2 = b.x.toBigInteger()
|
||||
const y2 = b.y.toBigInteger()
|
||||
const z2 = b.z
|
||||
const q = this.curve.q
|
||||
|
||||
const w1 = x1.multiply(z2).mod(q)
|
||||
const w2 = x2.multiply(z1).mod(q)
|
||||
const w3 = w1.subtract(w2)
|
||||
const w4 = y1.multiply(z2).mod(q)
|
||||
const w5 = y2.multiply(z1).mod(q)
|
||||
const w6 = w4.subtract(w5)
|
||||
|
||||
if (BigInteger.ZERO.equals(w3)) {
|
||||
if (BigInteger.ZERO.equals(w6)) {
|
||||
return this.twice() // this == b,计算自加
|
||||
}
|
||||
return this.curve.infinity // this == -b,则返回无穷远点
|
||||
}
|
||||
|
||||
const w7 = w1.add(w2)
|
||||
const w8 = z1.multiply(z2).mod(q)
|
||||
const w9 = w3.square().mod(q)
|
||||
const w10 = w3.multiply(w9).mod(q)
|
||||
const w11 = w8.multiply(w6.square()).subtract(w7.multiply(w9)).mod(q)
|
||||
|
||||
const x3 = w3.multiply(w11).mod(q)
|
||||
const y3 = w6.multiply(w9.multiply(w1).subtract(w11)).subtract(w4.multiply(w10)).mod(q)
|
||||
const z3 = w10.multiply(w8).mod(q)
|
||||
|
||||
return new ECPointFp(this.curve, this.curve.fromBigInteger(x3), this.curve.fromBigInteger(y3), z3)
|
||||
}
|
||||
|
||||
/**
|
||||
* 自加
|
||||
*
|
||||
* 标准射影坐标系:
|
||||
*
|
||||
* λ1 = 3 * x1^2 + a * z1^2
|
||||
* λ2 = 2 * y1 * z1
|
||||
* λ3 = y1^2
|
||||
* λ4 = λ3 * x1 * z1
|
||||
* λ5 = λ2^2
|
||||
* λ6 = λ1^2 − 8 * λ4
|
||||
* x3 = λ2 * λ6
|
||||
* y3 = λ1 * (4 * λ4 − λ6) − 2 * λ5 * λ3
|
||||
* z3 = λ2 * λ5
|
||||
*/
|
||||
twice() {
|
||||
if (this.isInfinity()) return this
|
||||
if (!this.y.toBigInteger().signum()) return this.curve.infinity
|
||||
|
||||
const x1 = this.x.toBigInteger()
|
||||
const y1 = this.y.toBigInteger()
|
||||
const z1 = this.z
|
||||
const q = this.curve.q
|
||||
const a = this.curve.a.toBigInteger()
|
||||
|
||||
const w1 = x1.square().multiply(THREE).add(a.multiply(z1.square())).mod(q)
|
||||
const w2 = y1.shiftLeft(1).multiply(z1).mod(q)
|
||||
const w3 = y1.square().mod(q)
|
||||
const w4 = w3.multiply(x1).multiply(z1).mod(q)
|
||||
const w5 = w2.square().mod(q)
|
||||
const w6 = w1.square().subtract(w4.shiftLeft(3)).mod(q)
|
||||
|
||||
const x3 = w2.multiply(w6).mod(q)
|
||||
const y3 = w1.multiply(w4.shiftLeft(2).subtract(w6)).subtract(w5.shiftLeft(1).multiply(w3)).mod(q)
|
||||
const z3 = w2.multiply(w5).mod(q)
|
||||
|
||||
return new ECPointFp(this.curve, this.curve.fromBigInteger(x3), this.curve.fromBigInteger(y3), z3)
|
||||
}
|
||||
|
||||
/**
|
||||
* 倍点计算
|
||||
*/
|
||||
multiply(k) {
|
||||
if (this.isInfinity()) return this
|
||||
if (!k.signum()) return this.curve.infinity
|
||||
|
||||
// 使用加减法
|
||||
const k3 = k.multiply(THREE)
|
||||
const neg = this.negate()
|
||||
let Q = this
|
||||
|
||||
for (let i = k3.bitLength() - 2; i > 0; i--) {
|
||||
Q = Q.twice()
|
||||
|
||||
const k3Bit = k3.testBit(i)
|
||||
const kBit = k.testBit(i)
|
||||
|
||||
if (k3Bit !== kBit) {
|
||||
Q = Q.add(k3Bit ? this : neg)
|
||||
}
|
||||
}
|
||||
|
||||
return Q
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 椭圆曲线 y^2 = x^3 + ax + b
|
||||
*/
|
||||
class ECCurveFp {
|
||||
constructor(q, a, b) {
|
||||
this.q = q
|
||||
this.a = this.fromBigInteger(a)
|
||||
this.b = this.fromBigInteger(b)
|
||||
this.infinity = new ECPointFp(this, null, null) // 无穷远点
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断两个椭圆曲线是否相等
|
||||
*/
|
||||
equals(other) {
|
||||
if (other === this) return true
|
||||
return (this.q.equals(other.q) && this.a.equals(other.a) && this.b.equals(other.b))
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成椭圆曲线域元素
|
||||
*/
|
||||
fromBigInteger(x) {
|
||||
return new ECFieldElementFp(this.q, x)
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 16 进制串为椭圆曲线点
|
||||
*/
|
||||
decodePointHex(s) {
|
||||
switch (parseInt(s.substr(0, 2), 16)) {
|
||||
// 第一个字节
|
||||
case 0:
|
||||
return this.infinity
|
||||
case 2:
|
||||
case 3:
|
||||
// 压缩
|
||||
const x = this.fromBigInteger(new BigInteger(s.substr(2), 16))
|
||||
// 对 p ≡ 3 (mod4),即存在正整数 u,使得 p = 4u + 3
|
||||
// 计算 y = (√ (x^3 + ax + b) % p)^(u + 1) modp
|
||||
let y = this.fromBigInteger(x.multiply(x.square()).add(
|
||||
x.multiply(this.a)
|
||||
).add(this.b).toBigInteger()
|
||||
.modPow(
|
||||
this.q.divide(new BigInteger('4')).add(BigInteger.ONE), this.q
|
||||
))
|
||||
// 算出结果 2 进制最后 1 位不等于第 1 个字节减 2 则取反
|
||||
if (!y.toBigInteger().mod(TWO).equals(new BigInteger(s.substr(0, 2), 16).subtract(TWO))) {
|
||||
y = y.negate()
|
||||
}
|
||||
return new ECPointFp(this, x, y)
|
||||
case 4:
|
||||
case 6:
|
||||
case 7:
|
||||
const len = (s.length - 2) / 2
|
||||
const xHex = s.substr(2, len)
|
||||
const yHex = s.substr(len + 2, len)
|
||||
|
||||
return new ECPointFp(this, this.fromBigInteger(new BigInteger(xHex, 16)), this.fromBigInteger(new BigInteger(yHex, 16)))
|
||||
default:
|
||||
// 不支持
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ECPointFp,
|
||||
ECCurveFp,
|
||||
}
|
||||
261
node_modules/sm-crypto/src/sm2/index.js
generated
vendored
261
node_modules/sm-crypto/src/sm2/index.js
generated
vendored
@@ -1,261 +0,0 @@
|
||||
/* eslint-disable no-use-before-define */
|
||||
const {BigInteger} = require('jsbn')
|
||||
const {encodeDer, decodeDer} = require('./asn1')
|
||||
const _ = require('./utils')
|
||||
const sm3 = require('./sm3').sm3
|
||||
|
||||
const {G, curve, n} = _.generateEcparam()
|
||||
const C1C2C3 = 0
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*/
|
||||
function doEncrypt(msg, publicKey, cipherMode = 1) {
|
||||
msg = typeof msg === 'string' ? _.hexToArray(_.utf8ToHex(msg)) : Array.prototype.slice.call(msg)
|
||||
publicKey = _.getGlobalCurve().decodePointHex(publicKey) // 先将公钥转成点
|
||||
|
||||
const keypair = _.generateKeyPairHex()
|
||||
const k = new BigInteger(keypair.privateKey, 16) // 随机数 k
|
||||
|
||||
// c1 = k * G
|
||||
let c1 = keypair.publicKey
|
||||
if (c1.length > 128) c1 = c1.substr(c1.length - 128)
|
||||
|
||||
// (x2, y2) = k * publicKey
|
||||
const p = publicKey.multiply(k)
|
||||
const x2 = _.hexToArray(_.leftPad(p.getX().toBigInteger().toRadix(16), 64))
|
||||
const y2 = _.hexToArray(_.leftPad(p.getY().toBigInteger().toRadix(16), 64))
|
||||
|
||||
// c3 = hash(x2 || msg || y2)
|
||||
const c3 = _.arrayToHex(sm3([].concat(x2, msg, y2)))
|
||||
|
||||
let ct = 1
|
||||
let offset = 0
|
||||
let t = [] // 256 位
|
||||
const z = [].concat(x2, y2)
|
||||
const nextT = () => {
|
||||
// (1) Hai = hash(z || ct)
|
||||
// (2) ct++
|
||||
t = sm3([...z, ct >> 24 & 0x00ff, ct >> 16 & 0x00ff, ct >> 8 & 0x00ff, ct & 0x00ff])
|
||||
ct++
|
||||
offset = 0
|
||||
}
|
||||
nextT() // 先生成 Ha1
|
||||
|
||||
for (let i = 0, len = msg.length; i < len; i++) {
|
||||
// t = Ha1 || Ha2 || Ha3 || Ha4
|
||||
if (offset === t.length) nextT()
|
||||
|
||||
// c2 = msg ^ t
|
||||
msg[i] ^= t[offset++] & 0xff
|
||||
}
|
||||
const c2 = _.arrayToHex(msg)
|
||||
|
||||
return cipherMode === C1C2C3 ? c1 + c2 + c3 : c1 + c3 + c2
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*/
|
||||
function doDecrypt(encryptData, privateKey, cipherMode = 1, {
|
||||
output = 'string',
|
||||
} = {}) {
|
||||
privateKey = new BigInteger(privateKey, 16)
|
||||
|
||||
let c3 = encryptData.substr(128, 64)
|
||||
let c2 = encryptData.substr(128 + 64)
|
||||
|
||||
if (cipherMode === C1C2C3) {
|
||||
c3 = encryptData.substr(encryptData.length - 64)
|
||||
c2 = encryptData.substr(128, encryptData.length - 128 - 64)
|
||||
}
|
||||
|
||||
const msg = _.hexToArray(c2)
|
||||
const c1 = _.getGlobalCurve().decodePointHex('04' + encryptData.substr(0, 128))
|
||||
|
||||
const p = c1.multiply(privateKey)
|
||||
const x2 = _.hexToArray(_.leftPad(p.getX().toBigInteger().toRadix(16), 64))
|
||||
const y2 = _.hexToArray(_.leftPad(p.getY().toBigInteger().toRadix(16), 64))
|
||||
|
||||
let ct = 1
|
||||
let offset = 0
|
||||
let t = [] // 256 位
|
||||
const z = [].concat(x2, y2)
|
||||
const nextT = () => {
|
||||
// (1) Hai = hash(z || ct)
|
||||
// (2) ct++
|
||||
t = sm3([...z, ct >> 24 & 0x00ff, ct >> 16 & 0x00ff, ct >> 8 & 0x00ff, ct & 0x00ff])
|
||||
ct++
|
||||
offset = 0
|
||||
}
|
||||
nextT() // 先生成 Ha1
|
||||
|
||||
for (let i = 0, len = msg.length; i < len; i++) {
|
||||
// t = Ha1 || Ha2 || Ha3 || Ha4
|
||||
if (offset === t.length) nextT()
|
||||
|
||||
// c2 = msg ^ t
|
||||
msg[i] ^= t[offset++] & 0xff
|
||||
}
|
||||
|
||||
// c3 = hash(x2 || msg || y2)
|
||||
const checkC3 = _.arrayToHex(sm3([].concat(x2, msg, y2)))
|
||||
|
||||
if (checkC3 === c3.toLowerCase()) {
|
||||
return output === 'array' ? msg : _.arrayToUtf8(msg)
|
||||
} else {
|
||||
return output === 'array' ? [] : ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 签名
|
||||
*/
|
||||
function doSignature(msg, privateKey, {
|
||||
pointPool, der, hash, publicKey, userId
|
||||
} = {}) {
|
||||
let hashHex = typeof msg === 'string' ? _.utf8ToHex(msg) : _.arrayToHex(msg)
|
||||
|
||||
if (hash) {
|
||||
// sm3杂凑
|
||||
publicKey = publicKey || getPublicKeyFromPrivateKey(privateKey)
|
||||
hashHex = getHash(hashHex, publicKey, userId)
|
||||
}
|
||||
|
||||
const dA = new BigInteger(privateKey, 16)
|
||||
const e = new BigInteger(hashHex, 16)
|
||||
|
||||
// k
|
||||
let k = null
|
||||
let r = null
|
||||
let s = null
|
||||
|
||||
do {
|
||||
do {
|
||||
let point
|
||||
if (pointPool && pointPool.length) {
|
||||
point = pointPool.pop()
|
||||
} else {
|
||||
point = getPoint()
|
||||
}
|
||||
k = point.k
|
||||
|
||||
// r = (e + x1) mod n
|
||||
r = e.add(point.x1).mod(n)
|
||||
} while (r.equals(BigInteger.ZERO) || r.add(k).equals(n))
|
||||
|
||||
// s = ((1 + dA)^-1 * (k - r * dA)) mod n
|
||||
s = dA.add(BigInteger.ONE).modInverse(n).multiply(k.subtract(r.multiply(dA))).mod(n)
|
||||
} while (s.equals(BigInteger.ZERO))
|
||||
|
||||
if (der) return encodeDer(r, s) // asn.1 der 编码
|
||||
|
||||
return _.leftPad(r.toString(16), 64) + _.leftPad(s.toString(16), 64)
|
||||
}
|
||||
|
||||
/**
|
||||
* 验签
|
||||
*/
|
||||
function doVerifySignature(msg, signHex, publicKey, {der, hash, userId} = {}) {
|
||||
let hashHex = typeof msg === 'string' ? _.utf8ToHex(msg) : _.arrayToHex(msg)
|
||||
|
||||
if (hash) {
|
||||
// sm3杂凑
|
||||
hashHex = getHash(hashHex, publicKey, userId)
|
||||
}
|
||||
|
||||
let r; let
|
||||
s
|
||||
if (der) {
|
||||
const decodeDerObj = decodeDer(signHex) // asn.1 der 解码
|
||||
r = decodeDerObj.r
|
||||
s = decodeDerObj.s
|
||||
} else {
|
||||
r = new BigInteger(signHex.substring(0, 64), 16)
|
||||
s = new BigInteger(signHex.substring(64), 16)
|
||||
}
|
||||
|
||||
const PA = curve.decodePointHex(publicKey)
|
||||
const e = new BigInteger(hashHex, 16)
|
||||
|
||||
// t = (r + s) mod n
|
||||
const t = r.add(s).mod(n)
|
||||
|
||||
if (t.equals(BigInteger.ZERO)) return false
|
||||
|
||||
// x1y1 = s * G + t * PA
|
||||
const x1y1 = G.multiply(s).add(PA.multiply(t))
|
||||
|
||||
// R = (e + x1) mod n
|
||||
const R = e.add(x1y1.getX().toBigInteger()).mod(n)
|
||||
|
||||
return r.equals(R)
|
||||
}
|
||||
|
||||
/**
|
||||
* sm3杂凑算法
|
||||
*/
|
||||
function getHash(hashHex, publicKey, userId = '1234567812345678') {
|
||||
// z = hash(entl || userId || a || b || gx || gy || px || py)
|
||||
userId = _.utf8ToHex(userId)
|
||||
const a = _.leftPad(G.curve.a.toBigInteger().toRadix(16), 64)
|
||||
const b = _.leftPad(G.curve.b.toBigInteger().toRadix(16), 64)
|
||||
const gx = _.leftPad(G.getX().toBigInteger().toRadix(16), 64)
|
||||
const gy = _.leftPad(G.getY().toBigInteger().toRadix(16), 64)
|
||||
let px
|
||||
let py
|
||||
if (publicKey.length === 128) {
|
||||
px = publicKey.substr(0, 64)
|
||||
py = publicKey.substr(64, 64)
|
||||
} else {
|
||||
const point = G.curve.decodePointHex(publicKey)
|
||||
px = _.leftPad(point.getX().toBigInteger().toRadix(16), 64)
|
||||
py = _.leftPad(point.getY().toBigInteger().toRadix(16), 64)
|
||||
}
|
||||
const data = _.hexToArray(userId + a + b + gx + gy + px + py)
|
||||
|
||||
const entl = userId.length * 4
|
||||
data.unshift(entl & 0x00ff)
|
||||
data.unshift(entl >> 8 & 0x00ff)
|
||||
|
||||
const z = sm3(data)
|
||||
|
||||
// e = hash(z || msg)
|
||||
return _.arrayToHex(sm3(z.concat(_.hexToArray(hashHex))))
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算公钥
|
||||
*/
|
||||
function getPublicKeyFromPrivateKey(privateKey) {
|
||||
const PA = G.multiply(new BigInteger(privateKey, 16))
|
||||
const x = _.leftPad(PA.getX().toBigInteger().toString(16), 64)
|
||||
const y = _.leftPad(PA.getY().toBigInteger().toString(16), 64)
|
||||
return '04' + x + y
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取椭圆曲线点
|
||||
*/
|
||||
function getPoint() {
|
||||
const keypair = _.generateKeyPairHex()
|
||||
const PA = curve.decodePointHex(keypair.publicKey)
|
||||
|
||||
keypair.k = new BigInteger(keypair.privateKey, 16)
|
||||
keypair.x1 = PA.getX().toBigInteger()
|
||||
|
||||
return keypair
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generateKeyPairHex: _.generateKeyPairHex,
|
||||
compressPublicKeyHex: _.compressPublicKeyHex,
|
||||
comparePublicKeyHex: _.comparePublicKeyHex,
|
||||
doEncrypt,
|
||||
doDecrypt,
|
||||
doSignature,
|
||||
doVerifySignature,
|
||||
getPublicKeyFromPrivateKey,
|
||||
getPoint,
|
||||
verifyPublicKey: _.verifyPublicKey,
|
||||
}
|
||||
170
node_modules/sm-crypto/src/sm2/sm3.js
generated
vendored
170
node_modules/sm-crypto/src/sm2/sm3.js
generated
vendored
@@ -1,170 +0,0 @@
|
||||
// 消息扩展
|
||||
const W = new Uint32Array(68)
|
||||
const M = new Uint32Array(64) // W'
|
||||
|
||||
/**
|
||||
* 循环左移
|
||||
*/
|
||||
function rotl(x, n) {
|
||||
const s = n & 31
|
||||
return (x << s) | (x >>> (32 - s))
|
||||
}
|
||||
|
||||
/**
|
||||
* 二进制异或运算
|
||||
*/
|
||||
function xor(x, y) {
|
||||
const result = []
|
||||
for (let i = x.length - 1; i >= 0; i--) result[i] = (x[i] ^ y[i]) & 0xff
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 压缩函数中的置换函数 P0(X) = X xor (X <<< 9) xor (X <<< 17)
|
||||
*/
|
||||
function P0(X) {
|
||||
return (X ^ rotl(X, 9)) ^ rotl(X, 17)
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息扩展中的置换函数 P1(X) = X xor (X <<< 15) xor (X <<< 23)
|
||||
*/
|
||||
function P1(X) {
|
||||
return (X ^ rotl(X, 15)) ^ rotl(X, 23)
|
||||
}
|
||||
|
||||
/**
|
||||
* sm3 本体
|
||||
*/
|
||||
function sm3(array) {
|
||||
let len = array.length * 8
|
||||
|
||||
// k 是满足 len + 1 + k = 448mod512 的最小的非负整数
|
||||
let k = len % 512
|
||||
// 如果 448 <= (512 % len) < 512,需要多补充 (len % 448) 比特'0'以满足总比特长度为512的倍数
|
||||
k = k >= 448 ? 512 - (k % 448) - 1 : 448 - k - 1
|
||||
|
||||
// 填充
|
||||
const kArr = new Array((k - 7) / 8)
|
||||
const lenArr = new Array(8)
|
||||
for (let i = 0, len = kArr.length; i < len; i++) kArr[i] = 0
|
||||
for (let i = 0, len = lenArr.length; i < len; i++) lenArr[i] = 0
|
||||
len = len.toString(2)
|
||||
for (let i = 7; i >= 0; i--) {
|
||||
if (len.length > 8) {
|
||||
const start = len.length - 8
|
||||
lenArr[i] = parseInt(len.substr(start), 2)
|
||||
len = len.substr(0, start)
|
||||
} else if (len.length > 0) {
|
||||
lenArr[i] = parseInt(len, 2)
|
||||
len = ''
|
||||
}
|
||||
}
|
||||
const m = new Uint8Array([...array, 0x80, ...kArr, ...lenArr])
|
||||
const dataView = new DataView(m.buffer, 0)
|
||||
|
||||
// 迭代压缩
|
||||
const n = m.length / 64
|
||||
const V = new Uint32Array([0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600, 0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e])
|
||||
for (let i = 0; i < n; i++) {
|
||||
W.fill(0)
|
||||
M.fill(0)
|
||||
|
||||
// 将消息分组B划分为 16 个字 W0, W1,……,W15
|
||||
const start = 16 * i
|
||||
for (let j = 0; j < 16; j++) {
|
||||
W[j] = dataView.getUint32((start + j) * 4, false)
|
||||
}
|
||||
|
||||
// W16 ~ W67:W[j] <- P1(W[j−16] xor W[j−9] xor (W[j−3] <<< 15)) xor (W[j−13] <<< 7) xor W[j−6]
|
||||
for (let j = 16; j < 68; j++) {
|
||||
W[j] = (P1((W[j - 16] ^ W[j - 9]) ^ rotl(W[j - 3], 15)) ^ rotl(W[j - 13], 7)) ^ W[j - 6]
|
||||
}
|
||||
|
||||
// W′0 ~ W′63:W′[j] = W[j] xor W[j+4]
|
||||
for (let j = 0; j < 64; j++) {
|
||||
M[j] = W[j] ^ W[j + 4]
|
||||
}
|
||||
|
||||
// 压缩
|
||||
const T1 = 0x79cc4519
|
||||
const T2 = 0x7a879d8a
|
||||
// 字寄存器
|
||||
let A = V[0]
|
||||
let B = V[1]
|
||||
let C = V[2]
|
||||
let D = V[3]
|
||||
let E = V[4]
|
||||
let F = V[5]
|
||||
let G = V[6]
|
||||
let H = V[7]
|
||||
// 中间变量
|
||||
let SS1
|
||||
let SS2
|
||||
let TT1
|
||||
let TT2
|
||||
let T
|
||||
for (let j = 0; j < 64; j++) {
|
||||
T = j >= 0 && j <= 15 ? T1 : T2
|
||||
SS1 = rotl(rotl(A, 12) + E + rotl(T, j), 7)
|
||||
SS2 = SS1 ^ rotl(A, 12)
|
||||
|
||||
TT1 = (j >= 0 && j <= 15 ? ((A ^ B) ^ C) : (((A & B) | (A & C)) | (B & C))) + D + SS2 + M[j]
|
||||
TT2 = (j >= 0 && j <= 15 ? ((E ^ F) ^ G) : ((E & F) | ((~E) & G))) + H + SS1 + W[j]
|
||||
|
||||
D = C
|
||||
C = rotl(B, 9)
|
||||
B = A
|
||||
A = TT1
|
||||
H = G
|
||||
G = rotl(F, 19)
|
||||
F = E
|
||||
E = P0(TT2)
|
||||
}
|
||||
|
||||
V[0] ^= A
|
||||
V[1] ^= B
|
||||
V[2] ^= C
|
||||
V[3] ^= D
|
||||
V[4] ^= E
|
||||
V[5] ^= F
|
||||
V[6] ^= G
|
||||
V[7] ^= H
|
||||
}
|
||||
|
||||
// 转回 uint8
|
||||
const result = []
|
||||
for (let i = 0, len = V.length; i < len; i++) {
|
||||
const word = V[i]
|
||||
result.push((word & 0xff000000) >>> 24, (word & 0xff0000) >>> 16, (word & 0xff00) >>> 8, word & 0xff)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* hmac 实现
|
||||
*/
|
||||
const blockLen = 64
|
||||
const iPad = new Uint8Array(blockLen)
|
||||
const oPad = new Uint8Array(blockLen)
|
||||
for (let i = 0; i < blockLen; i++) {
|
||||
iPad[i] = 0x36
|
||||
oPad[i] = 0x5c
|
||||
}
|
||||
function hmac(input, key) {
|
||||
// 密钥填充
|
||||
if (key.length > blockLen) key = sm3(key)
|
||||
while (key.length < blockLen) key.push(0)
|
||||
|
||||
const iPadKey = xor(key, iPad)
|
||||
const oPadKey = xor(key, oPad)
|
||||
|
||||
const hash = sm3([...iPadKey, ...input])
|
||||
return sm3([...oPadKey, ...hash])
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
sm3,
|
||||
hmac,
|
||||
}
|
||||
194
node_modules/sm-crypto/src/sm2/utils.js
generated
vendored
194
node_modules/sm-crypto/src/sm2/utils.js
generated
vendored
@@ -1,194 +0,0 @@
|
||||
/* eslint-disable no-bitwise, no-mixed-operators, no-use-before-define, max-len */
|
||||
const {BigInteger, SecureRandom} = require('jsbn')
|
||||
const {ECCurveFp} = require('./ec')
|
||||
|
||||
const rng = new SecureRandom()
|
||||
const {curve, G, n} = generateEcparam()
|
||||
|
||||
/**
|
||||
* 获取公共椭圆曲线
|
||||
*/
|
||||
function getGlobalCurve() {
|
||||
return curve
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成ecparam
|
||||
*/
|
||||
function generateEcparam() {
|
||||
// 椭圆曲线
|
||||
const p = new BigInteger('FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF', 16)
|
||||
const a = new BigInteger('FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC', 16)
|
||||
const b = new BigInteger('28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93', 16)
|
||||
const curve = new ECCurveFp(p, a, b)
|
||||
|
||||
// 基点
|
||||
const gxHex = '32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7'
|
||||
const gyHex = 'BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0'
|
||||
const G = curve.decodePointHex('04' + gxHex + gyHex)
|
||||
|
||||
const n = new BigInteger('FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123', 16)
|
||||
|
||||
return {curve, G, n}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成密钥对:publicKey = privateKey * G
|
||||
*/
|
||||
function generateKeyPairHex(a, b, c) {
|
||||
const random = a ? new BigInteger(a, b, c) : new BigInteger(n.bitLength(), rng)
|
||||
const d = random.mod(n.subtract(BigInteger.ONE)).add(BigInteger.ONE) // 随机数
|
||||
const privateKey = leftPad(d.toString(16), 64)
|
||||
|
||||
const P = G.multiply(d) // P = dG,p 为公钥,d 为私钥
|
||||
const Px = leftPad(P.getX().toBigInteger().toString(16), 64)
|
||||
const Py = leftPad(P.getY().toBigInteger().toString(16), 64)
|
||||
const publicKey = '04' + Px + Py
|
||||
|
||||
return {privateKey, publicKey}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成压缩公钥
|
||||
*/
|
||||
function compressPublicKeyHex(s) {
|
||||
if (s.length !== 130) throw new Error('Invalid public key to compress')
|
||||
|
||||
const len = (s.length - 2) / 2
|
||||
const xHex = s.substr(2, len)
|
||||
const y = new BigInteger(s.substr(len + 2, len), 16)
|
||||
|
||||
let prefix = '03'
|
||||
if (y.mod(new BigInteger('2')).equals(BigInteger.ZERO)) prefix = '02'
|
||||
|
||||
return prefix + xHex
|
||||
}
|
||||
|
||||
/**
|
||||
* utf8串转16进制串
|
||||
*/
|
||||
function utf8ToHex(input) {
|
||||
input = unescape(encodeURIComponent(input))
|
||||
|
||||
const length = input.length
|
||||
|
||||
// 转换到字数组
|
||||
const words = []
|
||||
for (let i = 0; i < length; i++) {
|
||||
words[i >>> 2] |= (input.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8)
|
||||
}
|
||||
|
||||
// 转换到16进制
|
||||
const hexChars = []
|
||||
for (let i = 0; i < length; i++) {
|
||||
const bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff
|
||||
hexChars.push((bite >>> 4).toString(16))
|
||||
hexChars.push((bite & 0x0f).toString(16))
|
||||
}
|
||||
|
||||
return hexChars.join('')
|
||||
}
|
||||
|
||||
/**
|
||||
* 补全16进制字符串
|
||||
*/
|
||||
function leftPad(input, num) {
|
||||
if (input.length >= num) return input
|
||||
|
||||
return (new Array(num - input.length + 1)).join('0') + input
|
||||
}
|
||||
|
||||
/**
|
||||
* 转成16进制串
|
||||
*/
|
||||
function arrayToHex(arr) {
|
||||
return arr.map(item => {
|
||||
item = item.toString(16)
|
||||
return item.length === 1 ? '0' + item : item
|
||||
}).join('')
|
||||
}
|
||||
|
||||
/**
|
||||
* 转成utf8串
|
||||
*/
|
||||
function arrayToUtf8(arr) {
|
||||
const words = []
|
||||
let j = 0
|
||||
for (let i = 0; i < arr.length * 2; i += 2) {
|
||||
words[i >>> 3] |= parseInt(arr[j], 10) << (24 - (i % 8) * 4)
|
||||
j++
|
||||
}
|
||||
|
||||
try {
|
||||
const latin1Chars = []
|
||||
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff
|
||||
latin1Chars.push(String.fromCharCode(bite))
|
||||
}
|
||||
|
||||
return decodeURIComponent(escape(latin1Chars.join('')))
|
||||
} catch (e) {
|
||||
throw new Error('Malformed UTF-8 data')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转成字节数组
|
||||
*/
|
||||
function hexToArray(hexStr) {
|
||||
const words = []
|
||||
let hexStrLength = hexStr.length
|
||||
|
||||
if (hexStrLength % 2 !== 0) {
|
||||
hexStr = leftPad(hexStr, hexStrLength + 1)
|
||||
}
|
||||
|
||||
hexStrLength = hexStr.length
|
||||
|
||||
for (let i = 0; i < hexStrLength; i += 2) {
|
||||
words.push(parseInt(hexStr.substr(i, 2), 16))
|
||||
}
|
||||
return words
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证公钥是否为椭圆曲线上的点
|
||||
*/
|
||||
function verifyPublicKey(publicKey) {
|
||||
const point = curve.decodePointHex(publicKey)
|
||||
if (!point) return false
|
||||
|
||||
const x = point.getX()
|
||||
const y = point.getY()
|
||||
|
||||
// 验证 y^2 是否等于 x^3 + ax + b
|
||||
return y.square().equals(x.multiply(x.square()).add(x.multiply(curve.a)).add(curve.b))
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证公钥是否等价,等价返回true
|
||||
*/
|
||||
function comparePublicKeyHex(publicKey1, publicKey2) {
|
||||
const point1 = curve.decodePointHex(publicKey1)
|
||||
if (!point1) return false
|
||||
|
||||
const point2 = curve.decodePointHex(publicKey2)
|
||||
if (!point2) return false
|
||||
|
||||
return point1.equals(point2)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getGlobalCurve,
|
||||
generateEcparam,
|
||||
generateKeyPairHex,
|
||||
compressPublicKeyHex,
|
||||
utf8ToHex,
|
||||
leftPad,
|
||||
arrayToHex,
|
||||
arrayToUtf8,
|
||||
hexToArray,
|
||||
verifyPublicKey,
|
||||
comparePublicKeyHex,
|
||||
}
|
||||
94
node_modules/sm-crypto/src/sm3/index.js
generated
vendored
94
node_modules/sm-crypto/src/sm3/index.js
generated
vendored
@@ -1,94 +0,0 @@
|
||||
const {sm3, hmac} = require('../sm2/sm3')
|
||||
|
||||
/**
|
||||
* 补全16进制字符串
|
||||
*/
|
||||
function leftPad(input, num) {
|
||||
if (input.length >= num) return input
|
||||
|
||||
return (new Array(num - input.length + 1)).join('0') + input
|
||||
}
|
||||
|
||||
/**
|
||||
* 字节数组转 16 进制串
|
||||
*/
|
||||
function ArrayToHex(arr) {
|
||||
return arr.map(item => {
|
||||
item = item.toString(16)
|
||||
return item.length === 1 ? '0' + item : item
|
||||
}).join('')
|
||||
}
|
||||
|
||||
/**
|
||||
* 转成字节数组
|
||||
*/
|
||||
function hexToArray(hexStr) {
|
||||
const words = []
|
||||
let hexStrLength = hexStr.length
|
||||
|
||||
if (hexStrLength % 2 !== 0) {
|
||||
hexStr = leftPad(hexStr, hexStrLength + 1)
|
||||
}
|
||||
|
||||
hexStrLength = hexStr.length
|
||||
|
||||
for (let i = 0; i < hexStrLength; i += 2) {
|
||||
words.push(parseInt(hexStr.substr(i, 2), 16))
|
||||
}
|
||||
return words
|
||||
}
|
||||
|
||||
/**
|
||||
* utf8 串转字节数组
|
||||
*/
|
||||
function utf8ToArray(str) {
|
||||
const arr = []
|
||||
|
||||
for (let i = 0, len = str.length; i < len; i++) {
|
||||
const point = str.codePointAt(i)
|
||||
|
||||
if (point <= 0x007f) {
|
||||
// 单字节,标量值:00000000 00000000 0zzzzzzz
|
||||
arr.push(point)
|
||||
} else if (point <= 0x07ff) {
|
||||
// 双字节,标量值:00000000 00000yyy yyzzzzzz
|
||||
arr.push(0xc0 | (point >>> 6)) // 110yyyyy(0xc0-0xdf)
|
||||
arr.push(0x80 | (point & 0x3f)) // 10zzzzzz(0x80-0xbf)
|
||||
} else if (point <= 0xD7FF || (point >= 0xE000 && point <= 0xFFFF)) {
|
||||
// 三字节:标量值:00000000 xxxxyyyy yyzzzzzz
|
||||
arr.push(0xe0 | (point >>> 12)) // 1110xxxx(0xe0-0xef)
|
||||
arr.push(0x80 | ((point >>> 6) & 0x3f)) // 10yyyyyy(0x80-0xbf)
|
||||
arr.push(0x80 | (point & 0x3f)) // 10zzzzzz(0x80-0xbf)
|
||||
} else if (point >= 0x010000 && point <= 0x10FFFF) {
|
||||
// 四字节:标量值:000wwwxx xxxxyyyy yyzzzzzz
|
||||
i++
|
||||
arr.push((0xf0 | (point >>> 18) & 0x1c)) // 11110www(0xf0-0xf7)
|
||||
arr.push((0x80 | ((point >>> 12) & 0x3f))) // 10xxxxxx(0x80-0xbf)
|
||||
arr.push((0x80 | ((point >>> 6) & 0x3f))) // 10yyyyyy(0x80-0xbf)
|
||||
arr.push((0x80 | (point & 0x3f))) // 10zzzzzz(0x80-0xbf)
|
||||
} else {
|
||||
// 五、六字节,暂时不支持
|
||||
arr.push(point)
|
||||
throw new Error('input is not supported')
|
||||
}
|
||||
}
|
||||
|
||||
return arr
|
||||
}
|
||||
|
||||
module.exports = function (input, options) {
|
||||
input = typeof input === 'string' ? utf8ToArray(input) : Array.prototype.slice.call(input)
|
||||
|
||||
if (options) {
|
||||
const mode = options.mode || 'hmac'
|
||||
if (mode !== 'hmac') throw new Error('invalid mode')
|
||||
|
||||
let key = options.key
|
||||
if (!key) throw new Error('invalid key')
|
||||
|
||||
key = typeof key === 'string' ? hexToArray(key) : Array.prototype.slice.call(key)
|
||||
return ArrayToHex(hmac(input, key))
|
||||
}
|
||||
|
||||
return ArrayToHex(sm3(input))
|
||||
}
|
||||
359
node_modules/sm-crypto/src/sm4/index.js
generated
vendored
359
node_modules/sm-crypto/src/sm4/index.js
generated
vendored
@@ -1,359 +0,0 @@
|
||||
/* eslint-disable no-bitwise, no-mixed-operators, complexity */
|
||||
const DECRYPT = 0
|
||||
const ROUND = 32
|
||||
const BLOCK = 16
|
||||
|
||||
const Sbox = [
|
||||
0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
|
||||
0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
|
||||
0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
|
||||
0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
|
||||
0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
|
||||
0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
|
||||
0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
|
||||
0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
|
||||
0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
|
||||
0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
|
||||
0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
|
||||
0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
|
||||
0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
|
||||
0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
|
||||
0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
|
||||
0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48
|
||||
]
|
||||
|
||||
const CK = [
|
||||
0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
|
||||
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
|
||||
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
|
||||
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
|
||||
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
|
||||
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
|
||||
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
|
||||
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
|
||||
]
|
||||
|
||||
/**
|
||||
* 16 进制串转字节数组
|
||||
*/
|
||||
function hexToArray(str) {
|
||||
const arr = []
|
||||
for (let i = 0, len = str.length; i < len; i += 2) {
|
||||
arr.push(parseInt(str.substr(i, 2), 16))
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
/**
|
||||
* 字节数组转 16 进制串
|
||||
*/
|
||||
function ArrayToHex(arr) {
|
||||
return arr.map(item => {
|
||||
item = item.toString(16)
|
||||
return item.length === 1 ? '0' + item : item
|
||||
}).join('')
|
||||
}
|
||||
|
||||
/**
|
||||
* utf8 串转字节数组
|
||||
*/
|
||||
function utf8ToArray(str) {
|
||||
const arr = []
|
||||
|
||||
for (let i = 0, len = str.length; i < len; i++) {
|
||||
const point = str.codePointAt(i)
|
||||
|
||||
if (point <= 0x007f) {
|
||||
// 单字节,标量值:00000000 00000000 0zzzzzzz
|
||||
arr.push(point)
|
||||
} else if (point <= 0x07ff) {
|
||||
// 双字节,标量值:00000000 00000yyy yyzzzzzz
|
||||
arr.push(0xc0 | (point >>> 6)) // 110yyyyy(0xc0-0xdf)
|
||||
arr.push(0x80 | (point & 0x3f)) // 10zzzzzz(0x80-0xbf)
|
||||
} else if (point <= 0xD7FF || (point >= 0xE000 && point <= 0xFFFF)) {
|
||||
// 三字节:标量值:00000000 xxxxyyyy yyzzzzzz
|
||||
arr.push(0xe0 | (point >>> 12)) // 1110xxxx(0xe0-0xef)
|
||||
arr.push(0x80 | ((point >>> 6) & 0x3f)) // 10yyyyyy(0x80-0xbf)
|
||||
arr.push(0x80 | (point & 0x3f)) // 10zzzzzz(0x80-0xbf)
|
||||
} else if (point >= 0x010000 && point <= 0x10FFFF) {
|
||||
// 四字节:标量值:000wwwxx xxxxyyyy yyzzzzzz
|
||||
i++
|
||||
arr.push((0xf0 | (point >>> 18) & 0x1c)) // 11110www(0xf0-0xf7)
|
||||
arr.push((0x80 | ((point >>> 12) & 0x3f))) // 10xxxxxx(0x80-0xbf)
|
||||
arr.push((0x80 | ((point >>> 6) & 0x3f))) // 10yyyyyy(0x80-0xbf)
|
||||
arr.push((0x80 | (point & 0x3f))) // 10zzzzzz(0x80-0xbf)
|
||||
} else {
|
||||
// 五、六字节,暂时不支持
|
||||
arr.push(point)
|
||||
throw new Error('input is not supported')
|
||||
}
|
||||
}
|
||||
|
||||
return arr
|
||||
}
|
||||
|
||||
/**
|
||||
* 字节数组转 utf8 串
|
||||
*/
|
||||
function arrayToUtf8(arr) {
|
||||
const str = []
|
||||
for (let i = 0, len = arr.length; i < len; i++) {
|
||||
if (arr[i] >= 0xf0 && arr[i] <= 0xf7) {
|
||||
// 四字节
|
||||
str.push(String.fromCodePoint(((arr[i] & 0x07) << 18) + ((arr[i + 1] & 0x3f) << 12) + ((arr[i + 2] & 0x3f) << 6) + (arr[i + 3] & 0x3f)))
|
||||
i += 3
|
||||
} else if (arr[i] >= 0xe0 && arr[i] <= 0xef) {
|
||||
// 三字节
|
||||
str.push(String.fromCodePoint(((arr[i] & 0x0f) << 12) + ((arr[i + 1] & 0x3f) << 6) + (arr[i + 2] & 0x3f)))
|
||||
i += 2
|
||||
} else if (arr[i] >= 0xc0 && arr[i] <= 0xdf) {
|
||||
// 双字节
|
||||
str.push(String.fromCodePoint(((arr[i] & 0x1f) << 6) + (arr[i + 1] & 0x3f)))
|
||||
i++
|
||||
} else {
|
||||
// 单字节
|
||||
str.push(String.fromCodePoint(arr[i]))
|
||||
}
|
||||
}
|
||||
|
||||
return str.join('')
|
||||
}
|
||||
|
||||
/**
|
||||
* 32 比特循环左移
|
||||
*/
|
||||
function rotl(x, n) {
|
||||
const s = n & 31
|
||||
return (x << s) | (x >>> (32 - s))
|
||||
}
|
||||
|
||||
/**
|
||||
* 非线性变换
|
||||
*/
|
||||
function byteSub(a) {
|
||||
return (Sbox[a >>> 24 & 0xFF] & 0xFF) << 24 |
|
||||
(Sbox[a >>> 16 & 0xFF] & 0xFF) << 16 |
|
||||
(Sbox[a >>> 8 & 0xFF] & 0xFF) << 8 |
|
||||
(Sbox[a & 0xFF] & 0xFF)
|
||||
}
|
||||
|
||||
/**
|
||||
* 线性变换,加密/解密用
|
||||
*/
|
||||
function l1(b) {
|
||||
return b ^ rotl(b, 2) ^ rotl(b, 10) ^ rotl(b, 18) ^ rotl(b, 24)
|
||||
}
|
||||
|
||||
/**
|
||||
* 线性变换,生成轮密钥用
|
||||
*/
|
||||
function l2(b) {
|
||||
return b ^ rotl(b, 13) ^ rotl(b, 23)
|
||||
}
|
||||
|
||||
/**
|
||||
* 以一组 128 比特进行加密/解密操作
|
||||
*/
|
||||
function sms4Crypt(input, output, roundKey) {
|
||||
const x = new Array(4)
|
||||
|
||||
// 字节数组转成字数组(此处 1 字 = 32 比特)
|
||||
const tmp = new Array(4)
|
||||
for (let i = 0; i < 4; i++) {
|
||||
tmp[0] = input[4 * i] & 0xff
|
||||
tmp[1] = input[4 * i + 1] & 0xff
|
||||
tmp[2] = input[4 * i + 2] & 0xff
|
||||
tmp[3] = input[4 * i + 3] & 0xff
|
||||
x[i] = tmp[0] << 24 | tmp[1] << 16 | tmp[2] << 8 | tmp[3]
|
||||
}
|
||||
|
||||
// x[i + 4] = x[i] ^ l1(byteSub(x[i + 1] ^ x[i + 2] ^ x[i + 3] ^ roundKey[i]))
|
||||
for (let r = 0, mid; r < 32; r += 4) {
|
||||
mid = x[1] ^ x[2] ^ x[3] ^ roundKey[r + 0]
|
||||
x[0] ^= l1(byteSub(mid)) // x[4]
|
||||
|
||||
mid = x[2] ^ x[3] ^ x[0] ^ roundKey[r + 1]
|
||||
x[1] ^= l1(byteSub(mid)) // x[5]
|
||||
|
||||
mid = x[3] ^ x[0] ^ x[1] ^ roundKey[r + 2]
|
||||
x[2] ^= l1(byteSub(mid)) // x[6]
|
||||
|
||||
mid = x[0] ^ x[1] ^ x[2] ^ roundKey[r + 3]
|
||||
x[3] ^= l1(byteSub(mid)) // x[7]
|
||||
}
|
||||
|
||||
// 反序变换
|
||||
for (let j = 0; j < 16; j += 4) {
|
||||
output[j] = x[3 - j / 4] >>> 24 & 0xff
|
||||
output[j + 1] = x[3 - j / 4] >>> 16 & 0xff
|
||||
output[j + 2] = x[3 - j / 4] >>> 8 & 0xff
|
||||
output[j + 3] = x[3 - j / 4] & 0xff
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 密钥扩展算法
|
||||
*/
|
||||
function sms4KeyExt(key, roundKey, cryptFlag) {
|
||||
const x = new Array(4)
|
||||
|
||||
// 字节数组转成字数组(此处 1 字 = 32 比特)
|
||||
const tmp = new Array(4)
|
||||
for (let i = 0; i < 4; i++) {
|
||||
tmp[0] = key[0 + 4 * i] & 0xff
|
||||
tmp[1] = key[1 + 4 * i] & 0xff
|
||||
tmp[2] = key[2 + 4 * i] & 0xff
|
||||
tmp[3] = key[3 + 4 * i] & 0xff
|
||||
x[i] = tmp[0] << 24 | tmp[1] << 16 | tmp[2] << 8 | tmp[3]
|
||||
}
|
||||
|
||||
// 与系统参数做异或
|
||||
x[0] ^= 0xa3b1bac6
|
||||
x[1] ^= 0x56aa3350
|
||||
x[2] ^= 0x677d9197
|
||||
x[3] ^= 0xb27022dc
|
||||
|
||||
// roundKey[i] = x[i + 4] = x[i] ^ l2(byteSub(x[i + 1] ^ x[i + 2] ^ x[i + 3] ^ CK[i]))
|
||||
for (let r = 0, mid; r < 32; r += 4) {
|
||||
mid = x[1] ^ x[2] ^ x[3] ^ CK[r + 0]
|
||||
roundKey[r + 0] = x[0] ^= l2(byteSub(mid)) // x[4]
|
||||
|
||||
mid = x[2] ^ x[3] ^ x[0] ^ CK[r + 1]
|
||||
roundKey[r + 1] = x[1] ^= l2(byteSub(mid)) // x[5]
|
||||
|
||||
mid = x[3] ^ x[0] ^ x[1] ^ CK[r + 2]
|
||||
roundKey[r + 2] = x[2] ^= l2(byteSub(mid)) // x[6]
|
||||
|
||||
mid = x[0] ^ x[1] ^ x[2] ^ CK[r + 3]
|
||||
roundKey[r + 3] = x[3] ^= l2(byteSub(mid)) // x[7]
|
||||
}
|
||||
|
||||
// 解密时使用反序的轮密钥
|
||||
if (cryptFlag === DECRYPT) {
|
||||
for (let r = 0, mid; r < 16; r++) {
|
||||
mid = roundKey[r]
|
||||
roundKey[r] = roundKey[31 - r]
|
||||
roundKey[31 - r] = mid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sm4(inArray, key, cryptFlag, {
|
||||
padding = 'pkcs#7', mode, iv = [], output = 'string'
|
||||
} = {}) {
|
||||
if (mode === 'cbc') {
|
||||
// CBC 模式,默认走 ECB 模式
|
||||
if (typeof iv === 'string') iv = hexToArray(iv)
|
||||
if (iv.length !== (128 / 8)) {
|
||||
// iv 不是 128 比特
|
||||
throw new Error('iv is invalid')
|
||||
}
|
||||
}
|
||||
|
||||
// 检查 key
|
||||
if (typeof key === 'string') key = hexToArray(key)
|
||||
if (key.length !== (128 / 8)) {
|
||||
// key 不是 128 比特
|
||||
throw new Error('key is invalid')
|
||||
}
|
||||
|
||||
// 检查输入
|
||||
if (typeof inArray === 'string') {
|
||||
if (cryptFlag !== DECRYPT) {
|
||||
// 加密,输入为 utf8 串
|
||||
inArray = utf8ToArray(inArray)
|
||||
} else {
|
||||
// 解密,输入为 16 进制串
|
||||
inArray = hexToArray(inArray)
|
||||
}
|
||||
} else {
|
||||
inArray = [...inArray]
|
||||
}
|
||||
|
||||
// 新增填充,sm4 是 16 个字节一个分组,所以统一走到 pkcs#7
|
||||
if ((padding === 'pkcs#5' || padding === 'pkcs#7') && cryptFlag !== DECRYPT) {
|
||||
const paddingCount = BLOCK - inArray.length % BLOCK
|
||||
for (let i = 0; i < paddingCount; i++) inArray.push(paddingCount)
|
||||
}
|
||||
|
||||
// 生成轮密钥
|
||||
const roundKey = new Array(ROUND)
|
||||
sms4KeyExt(key, roundKey, cryptFlag)
|
||||
|
||||
const outArray = []
|
||||
let lastVector = iv
|
||||
let restLen = inArray.length
|
||||
let point = 0
|
||||
while (restLen >= BLOCK) {
|
||||
const input = inArray.slice(point, point + 16)
|
||||
const output = new Array(16)
|
||||
|
||||
if (mode === 'cbc') {
|
||||
for (let i = 0; i < BLOCK; i++) {
|
||||
if (cryptFlag !== DECRYPT) {
|
||||
// 加密过程在组加密前进行异或
|
||||
input[i] ^= lastVector[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sms4Crypt(input, output, roundKey)
|
||||
|
||||
|
||||
for (let i = 0; i < BLOCK; i++) {
|
||||
if (mode === 'cbc') {
|
||||
if (cryptFlag === DECRYPT) {
|
||||
// 解密过程在组解密后进行异或
|
||||
output[i] ^= lastVector[i]
|
||||
}
|
||||
}
|
||||
|
||||
outArray[point + i] = output[i]
|
||||
}
|
||||
|
||||
if (mode === 'cbc') {
|
||||
if (cryptFlag !== DECRYPT) {
|
||||
// 使用上一次输出作为加密向量
|
||||
lastVector = output
|
||||
} else {
|
||||
// 使用上一次输入作为解密向量
|
||||
lastVector = input
|
||||
}
|
||||
}
|
||||
|
||||
restLen -= BLOCK
|
||||
point += BLOCK
|
||||
}
|
||||
|
||||
// 去除填充,sm4 是 16 个字节一个分组,所以统一走到 pkcs#7
|
||||
if ((padding === 'pkcs#5' || padding === 'pkcs#7') && cryptFlag === DECRYPT) {
|
||||
const len = outArray.length
|
||||
const paddingCount = outArray[len - 1]
|
||||
for (let i = 1; i <= paddingCount; i++) {
|
||||
if (outArray[len - i] !== paddingCount) throw new Error('padding is invalid')
|
||||
}
|
||||
outArray.splice(len - paddingCount, paddingCount)
|
||||
}
|
||||
|
||||
// 调整输出
|
||||
if (output !== 'array') {
|
||||
if (cryptFlag !== DECRYPT) {
|
||||
// 加密,输出转 16 进制串
|
||||
return ArrayToHex(outArray)
|
||||
} else {
|
||||
// 解密,输出转 utf8 串
|
||||
return arrayToUtf8(outArray)
|
||||
}
|
||||
} else {
|
||||
return outArray
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
encrypt(inArray, key, options) {
|
||||
return sm4(inArray, key, 1, options)
|
||||
},
|
||||
decrypt(inArray, key, options) {
|
||||
return sm4(inArray, key, 0, options)
|
||||
}
|
||||
}
|
||||
26
node_modules/sm-crypto/webpack.config.js
generated
vendored
26
node_modules/sm-crypto/webpack.config.js
generated
vendored
@@ -1,26 +0,0 @@
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
sm2: './src/sm2/index.js',
|
||||
sm3: './src/sm3/index.js',
|
||||
sm4: './src/sm4/index.js',
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: '[name].js',
|
||||
library: '[name]',
|
||||
libraryTarget: 'umd',
|
||||
},
|
||||
module: {
|
||||
loaders: [{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel-loader'
|
||||
}]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.optimize.UglifyJsPlugin(),
|
||||
]
|
||||
};
|
||||
40
package-lock.json
generated
40
package-lock.json
generated
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"name": "ks-app-employment-service",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"sm-crypto": "^0.3.13"
|
||||
}
|
||||
},
|
||||
"node_modules/jsbn": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
|
||||
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
|
||||
},
|
||||
"node_modules/sm-crypto": {
|
||||
"version": "0.3.13",
|
||||
"resolved": "https://registry.npmjs.org/sm-crypto/-/sm-crypto-0.3.13.tgz",
|
||||
"integrity": "sha512-ztNF+pZq6viCPMA1A6KKu3bgpkmYti5avykRHbcFIdSipFdkVmfUw2CnpM2kBJyppIalqvczLNM3wR8OQ0pT5w==",
|
||||
"dependencies": {
|
||||
"jsbn": "^1.1.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"jsbn": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
|
||||
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
|
||||
},
|
||||
"sm-crypto": {
|
||||
"version": "0.3.13",
|
||||
"resolved": "https://registry.npmjs.org/sm-crypto/-/sm-crypto-0.3.13.tgz",
|
||||
"integrity": "sha512-ztNF+pZq6viCPMA1A6KKu3bgpkmYti5avykRHbcFIdSipFdkVmfUw2CnpM2kBJyppIalqvczLNM3wR8OQ0pT5w==",
|
||||
"requires": {
|
||||
"jsbn": "^1.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"sm-crypto": "^0.3.13"
|
||||
}
|
||||
}
|
||||
@@ -1,329 +1,320 @@
|
||||
<template>
|
||||
<AppLayout title="" :use-scroll-view="false">
|
||||
<template #headerleft>
|
||||
<view class="btnback">
|
||||
<image src="@/static/icon/back.png" @click="navBack"></image>
|
||||
</view>
|
||||
</template>
|
||||
<template #headerright>
|
||||
<view class="btn mar_ri10">
|
||||
<image
|
||||
src="@/static/icon/collect3.png"
|
||||
v-if="!companyInfo.isCollection"
|
||||
></image>
|
||||
<image src="@/static/icon/collect2.png" v-else></image>
|
||||
</view>
|
||||
</template>
|
||||
<view class="content">
|
||||
<view class="content-top">
|
||||
<view class="companyinfo-left">
|
||||
<image src="@/static/icon/companyIcon.png" mode=""></image>
|
||||
</view>
|
||||
<view class="companyinfo-right">
|
||||
<view class="row1">{{ companyInfo?.companyName }}</view>
|
||||
<view class="row2">
|
||||
{{ companyInfo?.scale }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="conetent-info" :class="{ expanded: isExpanded }">
|
||||
<view class="info-title">公司介绍</view>
|
||||
<view class="info-desirption">{{
|
||||
companyInfo.companyIntroduction
|
||||
}}</view>
|
||||
<!-- <view class="info-title title2">公司地址</view>
|
||||
<view class="locationCompany"></view> -->
|
||||
</view>
|
||||
<view class="expand" @click="expand">
|
||||
<text>{{ isExpanded ? "收起" : "展开" }}</text>
|
||||
<image
|
||||
class="expand-img"
|
||||
:class="{ 'expand-img-active': !isExpanded }"
|
||||
src="@/static/icon/downs.png"
|
||||
></image>
|
||||
</view>
|
||||
<scroll-view scroll-y class="Detailscroll-view">
|
||||
<view class="views">
|
||||
<view class="Detail-title"><text class="title">在招职位</text></view>
|
||||
<template v-if="companyInfo.jobInfoList.length != 0">
|
||||
<view v-for="job in companyInfo.jobInfoList" :key="job.id">
|
||||
<view class="cards" @click="navTo(`/packageA/pages/post/post?jobId=${JSON.stringify(job)}`)">
|
||||
<view class="card-company">
|
||||
<text class="company">{{ job.jobTitle }}</text>
|
||||
<view class="salary"> {{ job.salaryRange }}元 </view>
|
||||
</view>
|
||||
<view class="card-companyName">{{ job.companyName }}</view>
|
||||
<view class="card-companyName">{{ job.jobDescription }}</view>
|
||||
<view class="card-tags">
|
||||
<view class="tag">
|
||||
{{ job.educationRequirement }}
|
||||
</view>
|
||||
<view class="tag">
|
||||
{{ job.experienceRequirement }}
|
||||
</view>
|
||||
<!-- <view class="tag">
|
||||
{{ vacanciesTo(job.vacancies) }}
|
||||
</view> -->
|
||||
<view class="tag" v-if="job.jobRequirement">
|
||||
{{ job.jobRequirement }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-bottom">
|
||||
<view>{{ job.postingDate }}</view>
|
||||
<view>
|
||||
<convert-distance
|
||||
:alat="job.latitude"
|
||||
:along="job.longitude"
|
||||
:blat="latitude"
|
||||
:blong="longitude"
|
||||
></convert-distance>
|
||||
<dict-Label
|
||||
class="mar_le10"
|
||||
dictType="area"
|
||||
:value="job.jobLocationAreaCode"
|
||||
></dict-Label>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<AppLayout title="" :use-scroll-view="false">
|
||||
<template #headerleft>
|
||||
<view class="btnback">
|
||||
<image src="@/static/icon/back.png" @click="navBack"></image>
|
||||
</view>
|
||||
</template>
|
||||
<empty v-else pdTop="200"></empty>
|
||||
</template>
|
||||
<template #headerright>
|
||||
<view class="btn mar_ri10">
|
||||
<image
|
||||
src="@/static/icon/collect3.png"
|
||||
v-if="!companyInfo.isCollection"
|
||||
@click="companyCollection"
|
||||
></image>
|
||||
<image src="@/static/icon/collect2.png" v-else @click="companyCollection"></image>
|
||||
</view>
|
||||
</template>
|
||||
<view class="content">
|
||||
<view class="content-top">
|
||||
<view class="companyinfo-left">
|
||||
<image src="@/static/icon/companyIcon.png" mode=""></image>
|
||||
</view>
|
||||
<view class="companyinfo-right">
|
||||
<view class="row1">{{ companyInfo?.name }}</view>
|
||||
<view class="row2">
|
||||
<dict-tree-Label
|
||||
v-if="companyInfo?.industry"
|
||||
dictType="industry"
|
||||
:value="companyInfo?.industry"
|
||||
></dict-tree-Label>
|
||||
<span v-if="companyInfo?.industry"> </span>
|
||||
<dict-Label dictType="scale" :value="companyInfo?.scale"></dict-Label>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="conetent-info" :class="{ expanded: isExpanded }">
|
||||
<view class="info-title">公司介绍</view>
|
||||
<view class="info-desirption">{{ companyInfo.description }}</view>
|
||||
<!-- <view class="info-title title2">公司地址</view>
|
||||
<view class="locationCompany"></view> -->
|
||||
</view>
|
||||
<view class="expand" @click="expand">
|
||||
<text>{{ isExpanded ? '收起' : '展开' }}</text>
|
||||
<image
|
||||
class="expand-img"
|
||||
:class="{ 'expand-img-active': !isExpanded }"
|
||||
src="@/static/icon/downs.png"
|
||||
></image>
|
||||
</view>
|
||||
<scroll-view scroll-y class="Detailscroll-view" @scrolltolower="getJobsList('add')">
|
||||
<view class="views">
|
||||
<view class="Detail-title"><text class="title">在招职位</text></view>
|
||||
<renderJobs
|
||||
v-if="pageState.list.length"
|
||||
:list="pageState.list"
|
||||
:longitude="longitudeVal"
|
||||
:latitude="latitudeVal"
|
||||
></renderJobs>
|
||||
<empty v-else pdTop="200"></empty>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</AppLayout>
|
||||
</AppLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import point from "@/static/icon/point.png";
|
||||
import { reactive, inject, watch, ref, onMounted, computed } from "vue";
|
||||
import { onLoad, onShow } from "@dcloudio/uni-app";
|
||||
import dictLabel from "@/components/dict-Label/dict-Label.vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import useLocationStore from "@/stores/useLocationStore";
|
||||
import point from '@/static/icon/point.png';
|
||||
import { reactive, inject, watch, ref, onMounted, computed } from 'vue';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import dictLabel from '@/components/dict-Label/dict-Label.vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import useLocationStore from '@/stores/useLocationStore';
|
||||
const { longitudeVal, latitudeVal } = storeToRefs(useLocationStore());
|
||||
const { $api, navTo, vacanciesTo, navBack } = inject("globalFunction");
|
||||
const { $api, navTo, vacanciesTo, navBack } = inject('globalFunction');
|
||||
|
||||
const isExpanded = ref(false);
|
||||
const pageState = reactive({
|
||||
page: 0,
|
||||
list: [],
|
||||
total: 0,
|
||||
maxPage: 1,
|
||||
pageSize: 10,
|
||||
page: 0,
|
||||
list: [],
|
||||
total: 0,
|
||||
maxPage: 1,
|
||||
pageSize: 10,
|
||||
});
|
||||
const companyInfo = ref({});
|
||||
|
||||
onLoad((options) => {
|
||||
companyInfo.value = JSON.parse(options.job);
|
||||
console.log(companyInfo.value, "companyInfo.value");
|
||||
console.log(options);
|
||||
getCompanyInfo(options.companyId || options.bussinessId);
|
||||
});
|
||||
|
||||
function companyCollection() {
|
||||
const companyId = companyInfo.value.companyId;
|
||||
if (companyInfo.value.isCollection) {
|
||||
$api.createRequest(`/app/company/collection/${companyId}`, {}, 'DELETE').then((resData) => {
|
||||
getCompanyInfo(companyId);
|
||||
$api.msg('取消收藏成功');
|
||||
});
|
||||
} else {
|
||||
$api.createRequest(`/app/company/collection/${companyId}`, {}, 'POST').then((resData) => {
|
||||
getCompanyInfo(companyId);
|
||||
$api.msg('收藏成功');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getCompanyInfo(id) {
|
||||
$api.createRequest(`/app/company/${id}`).then((resData) => {
|
||||
companyInfo.value = resData.data;
|
||||
getJobsList();
|
||||
});
|
||||
}
|
||||
|
||||
function getJobsList(type = 'add') {
|
||||
if (type === 'refresh') {
|
||||
pageState.page = 1;
|
||||
pageState.maxPage = 1;
|
||||
}
|
||||
if (type === 'add' && pageState.page < pageState.maxPage) {
|
||||
pageState.page += 1;
|
||||
}
|
||||
let params = {
|
||||
current: pageState.page,
|
||||
pageSize: pageState.pageSize,
|
||||
};
|
||||
$api.createRequest(`/app/company/job/${companyInfo.value.companyId}`, params).then((resData) => {
|
||||
const { rows, total } = resData;
|
||||
if (type === 'add') {
|
||||
const str = pageState.pageSize * (pageState.page - 1);
|
||||
const end = pageState.list.length;
|
||||
const reslist = rows;
|
||||
pageState.list.splice(str, end, ...reslist);
|
||||
} else {
|
||||
pageState.list = rows;
|
||||
}
|
||||
pageState.total = resData.total;
|
||||
pageState.maxPage = Math.ceil(pageState.total / pageState.pageSize);
|
||||
});
|
||||
}
|
||||
|
||||
function expand() {
|
||||
isExpanded.value = !isExpanded.value;
|
||||
isExpanded.value = !isExpanded.value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.btnback {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
.btnback{
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
}
|
||||
|
||||
image {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.content-top {
|
||||
padding: 28rpx;
|
||||
padding-top: 50rpx;
|
||||
.content{
|
||||
height: 100%
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
flex-direction: column
|
||||
.content-top{
|
||||
padding: 28rpx
|
||||
padding-top: 50rpx
|
||||
display: flex
|
||||
flex-direction: row
|
||||
flex-wrap: nowrap
|
||||
.companyinfo-left{
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
margin-right: 24rpx
|
||||
}
|
||||
.companyinfo-right{
|
||||
|
||||
.companyinfo-left {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
margin-right: 24rpx;
|
||||
.row1{
|
||||
font-weight: 500;
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
}
|
||||
.row2{
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #6C7282;
|
||||
line-height: 45rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.companyinfo-right {
|
||||
.row1 {
|
||||
font-weight: 500;
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.row2 {
|
||||
.conetent-info{
|
||||
padding: 0 28rpx
|
||||
overflow: hidden;
|
||||
max-height: 0rpx;
|
||||
transition: max-height 0.3s ease;
|
||||
.info-title{
|
||||
font-weight: 600;
|
||||
font-size: 28rpx;
|
||||
color: #000000;
|
||||
}
|
||||
.info-desirption{
|
||||
margin-top: 12rpx
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #495265;
|
||||
text-align: justified;
|
||||
}
|
||||
.title2{
|
||||
margin-top: 48rpx
|
||||
}
|
||||
}
|
||||
.expanded {
|
||||
max-height: 1000rpx; // 足够显示完整内容
|
||||
}
|
||||
.expand{
|
||||
display: flex
|
||||
flex-wrap: nowrap
|
||||
white-space: nowrap
|
||||
justify-content: center
|
||||
margin-top: 20rpx
|
||||
margin-bottom: 28rpx
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #6C7282;
|
||||
line-height: 45rpx;
|
||||
}
|
||||
color: #256BFA;
|
||||
.expand-img{
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.expand-img-active{
|
||||
transform: rotate(180deg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.conetent-info {
|
||||
padding: 0 28rpx;
|
||||
}
|
||||
.Detailscroll-view{
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
max-height: 0rpx;
|
||||
transition: max-height 0.3s ease;
|
||||
|
||||
.info-title {
|
||||
font-weight: 600;
|
||||
font-size: 28rpx;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.info-desirption {
|
||||
margin-top: 12rpx;
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #495265;
|
||||
text-align: justified;
|
||||
}
|
||||
|
||||
.title2 {
|
||||
margin-top: 48rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.expanded {
|
||||
max-height: 1000rpx; // 足够显示完整内容
|
||||
}
|
||||
|
||||
.expand {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
white-space: nowrap;
|
||||
justify-content: center;
|
||||
margin-top: 20rpx;
|
||||
margin-bottom: 28rpx;
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #256BFA;
|
||||
|
||||
.expand-img {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
.expand-img-active {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.Detailscroll-view {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
background: #F4F4F4;
|
||||
|
||||
.views {
|
||||
padding: 28rpx;
|
||||
|
||||
.Detail-title {
|
||||
font-weight: 600;
|
||||
font-size: 32rpx;
|
||||
color: #000000;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.title {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.Detail-title::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
left: -14rpx;
|
||||
bottom: 0;
|
||||
height: 16rpx;
|
||||
width: 108rpx;
|
||||
background: linear-gradient(to right, #CBDEFF, #FFFFFF);
|
||||
border-radius: 8rpx;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.cards {
|
||||
padding: 32rpx;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0rpx 0rpx 8rpx 0rpx rgba(0, 0, 0, 0.04);
|
||||
border-radius: 20rpx 20rpx 20rpx 20rpx;
|
||||
margin-top: 22rpx;
|
||||
|
||||
.card-company {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
|
||||
.company {
|
||||
font-weight: 500;
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
background: #F4F4F4;
|
||||
.views{
|
||||
padding: 28rpx
|
||||
.Detail-title{
|
||||
font-weight: 600;
|
||||
font-size: 32rpx;
|
||||
color: #000000;
|
||||
position: relative;
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
.title{
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.salary {
|
||||
font-weight: 500;
|
||||
font-size: 28rpx;
|
||||
color: #4C6EFB;
|
||||
white-space: nowrap;
|
||||
line-height: 48rpx;
|
||||
.Detail-title::before{
|
||||
position: absolute
|
||||
content: '';
|
||||
left: -14rpx
|
||||
bottom: 0
|
||||
height: 16rpx;
|
||||
width: 108rpx;
|
||||
background: linear-gradient(to right, #CBDEFF, #FFFFFF);
|
||||
border-radius: 8rpx;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.card-companyName {
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #6C7282;
|
||||
}
|
||||
|
||||
.card-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.tag {
|
||||
width: fit-content;
|
||||
height: 30rpx;
|
||||
background: #F4F4F4;
|
||||
border-radius: 4rpx;
|
||||
padding: 6rpx 20rpx;
|
||||
line-height: 30rpx;
|
||||
font-weight: 400;
|
||||
font-size: 24rpx;
|
||||
color: #6C7282;
|
||||
text-align: center;
|
||||
margin-top: 14rpx;
|
||||
white-space: nowrap;
|
||||
margin-right: 20rpx;
|
||||
.cards{
|
||||
padding: 32rpx;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0rpx 0rpx 8rpx 0rpx rgba(0,0,0,0.04);
|
||||
border-radius: 20rpx 20rpx 20rpx 20rpx;
|
||||
margin-top: 22rpx;
|
||||
.card-company{
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
align-items: flex-start
|
||||
.company{
|
||||
font-weight: 500;
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
}
|
||||
.salary{
|
||||
font-weight: 500;
|
||||
font-size: 28rpx;
|
||||
color: #4C6EFB;
|
||||
white-space: nowrap
|
||||
line-height: 48rpx
|
||||
}
|
||||
}
|
||||
.card-companyName{
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #6C7282;
|
||||
}
|
||||
.card-tags{
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
.tag{
|
||||
width: fit-content;
|
||||
height: 30rpx;
|
||||
background: #F4F4F4;
|
||||
border-radius: 4rpx;
|
||||
padding: 6rpx 20rpx;
|
||||
line-height: 30rpx;
|
||||
font-weight: 400;
|
||||
font-size: 24rpx;
|
||||
color: #6C7282;
|
||||
text-align: center;
|
||||
margin-top: 14rpx;
|
||||
white-space: nowrap
|
||||
margin-right: 20rpx
|
||||
}
|
||||
}
|
||||
.card-bottom{
|
||||
margin-top: 32rpx
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
font-size: 28rpx;
|
||||
color: #6C7282;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-bottom {
|
||||
margin-top: 32rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 28rpx;
|
||||
color: #6C7282;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
<template>
|
||||
<view class="page-container">
|
||||
<AppLayout
|
||||
title="添加工作经历"
|
||||
border
|
||||
back-gorund-color="#ffffff"
|
||||
:show-bg-image="false"
|
||||
>
|
||||
<template #headerleft>
|
||||
<view class="btn mar_le20 button-click" @click="navBack">取消</view>
|
||||
</template>
|
||||
<template #headerright>
|
||||
<view class="btn mar_ri20 button-click" @click="handleConfirm">确认</view>
|
||||
</template>
|
||||
<view class="content">
|
||||
<view class="content-input">
|
||||
<view class="input-titile">公司名称</view>
|
||||
@@ -33,12 +44,8 @@
|
||||
<textarea class="textarea-con" v-model="formData.description" placeholder-style="font-size: 16px" maxlength="500" placeholder="请输入工作描述"/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部确认按钮 -->
|
||||
<view class="bottom-confirm-btn">
|
||||
<view class="confirm-btn" @click="handleConfirm">确认</view>
|
||||
</view>
|
||||
</view>
|
||||
</AppLayout>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -199,6 +206,7 @@
|
||||
console.log('页面类型:', pageType.value);
|
||||
|
||||
let resData;
|
||||
alert(editData.value.id)
|
||||
// 根据页面类型调用不同的接口
|
||||
if (pageType.value === 'edit' && editData.value?.id) {
|
||||
// 编辑模式:调用更新接口
|
||||
@@ -226,18 +234,12 @@
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.page-container {
|
||||
min-height: 100vh;
|
||||
background-color: #ffffff;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.content{
|
||||
padding: 28rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
padding-bottom: 120rpx;
|
||||
justify-content: flex-start
|
||||
height: calc(100% - 120rpx)
|
||||
}
|
||||
.content-input
|
||||
margin-bottom: 52rpx
|
||||
@@ -326,30 +328,6 @@
|
||||
line-height: 20rpx
|
||||
display: flex
|
||||
align-items: center
|
||||
|
||||
// 底部确认按钮样式
|
||||
.bottom-confirm-btn
|
||||
position: fixed
|
||||
bottom: 0
|
||||
left: 0
|
||||
right: 0
|
||||
background-color: #ffffff
|
||||
padding: 20rpx 28rpx
|
||||
border-top: 2rpx solid #EBEBEB
|
||||
z-index: 999
|
||||
|
||||
.confirm-btn
|
||||
width: 100%
|
||||
height: 90rpx
|
||||
background: #256BFA
|
||||
border-radius: 12rpx
|
||||
font-weight: 500
|
||||
font-size: 32rpx
|
||||
color: #FFFFFF
|
||||
text-align: center
|
||||
line-height: 90rpx
|
||||
button-click: true
|
||||
|
||||
// .content-sex
|
||||
// height: 110rpx;
|
||||
// display: flex
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -31,7 +31,7 @@
|
||||
<input
|
||||
class="input-con triangle"
|
||||
disabled
|
||||
v-if="!state.jobsText || !state.jobsText.length"
|
||||
v-if="!state.jobsText.length"
|
||||
placeholder="请选择您的求职岗位"
|
||||
/>
|
||||
<view class="input-nx" @click="changeJobs" v-else>
|
||||
@@ -40,7 +40,6 @@
|
||||
</view>
|
||||
</view>
|
||||
<SelectJobs ref="selectJobsModel"></SelectJobs>
|
||||
<SelectPopup ref="selectPopupRef"></SelectPopup>
|
||||
</AppLayout>
|
||||
</template>
|
||||
|
||||
@@ -48,24 +47,16 @@
|
||||
import { reactive, inject, watch, ref, onMounted, computed } from 'vue';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import SelectJobs from '@/components/selectJobs/selectJobs.vue';
|
||||
import SelectPopup from '@/components/selectPopup/selectPopup.vue';
|
||||
const { $api, navTo, navBack, config } = inject('globalFunction');
|
||||
|
||||
// 创建本地的 openSelectPopup 函数
|
||||
const openSelectPopup = (config) => {
|
||||
if (selectPopupRef.value) {
|
||||
selectPopupRef.value.open(config);
|
||||
}
|
||||
};
|
||||
const openSelectPopup = inject('openSelectPopup');
|
||||
import { storeToRefs } from 'pinia';
|
||||
import useUserStore from '@/stores/useUserStore';
|
||||
import useDictStore from '@/stores/useDictStore';
|
||||
const { userInfo } = storeToRefs(useUserStore());
|
||||
const { getUserResume } = useUserStore();
|
||||
const { dictLabel, oneDictData, getDictData } = useDictStore();
|
||||
const { dictLabel, oneDictData } = useDictStore();
|
||||
|
||||
const selectJobsModel = ref();
|
||||
const selectPopupRef = ref();
|
||||
const percent = ref('0%');
|
||||
const salay = [2, 5, 10, 15, 20, 25, 30, 50, 80, 100];
|
||||
const state = reactive({
|
||||
@@ -81,9 +72,7 @@ const fromValue = reactive({
|
||||
area: '',
|
||||
jobTitleId: [],
|
||||
});
|
||||
onLoad(async () => {
|
||||
// 初始化字典数据
|
||||
await getDictData();
|
||||
onLoad(() => {
|
||||
initLoad();
|
||||
});
|
||||
const confirm = () => {
|
||||
@@ -110,12 +99,8 @@ function initLoad() {
|
||||
fromValue.jobTitleId = userInfo.value.jobTitleId;
|
||||
// 回显
|
||||
state.areaText = dictLabel('area', Number(userInfo.value.area));
|
||||
if (userInfo.value.salaryMin && userInfo.value.salaryMax) {
|
||||
state.salayText = `${userInfo.value.salaryMin}-${userInfo.value.salaryMax}`;
|
||||
} else {
|
||||
state.salayText = '';
|
||||
}
|
||||
state.jobsText = userInfo.value.jobTitle || [];
|
||||
state.salayText = `${userInfo.value.salaryMin}-${userInfo.value.salaryMax}`;
|
||||
state.jobsText = userInfo.value.jobTitle;
|
||||
const result = getFormCompletionPercent(fromValue);
|
||||
percent.value = result;
|
||||
}
|
||||
@@ -138,8 +123,7 @@ const changeSalary = () => {
|
||||
const copyri = JSON.parse(JSON.stringify(salay));
|
||||
const [lf, ri] = e.detail.value;
|
||||
const risalay = copyri.slice(lf, copyri.length);
|
||||
// 更新右侧选项
|
||||
state.risalay = risalay;
|
||||
this.setColunm(1, risalay);
|
||||
leftIndex = salayData[0];
|
||||
}
|
||||
},
|
||||
|
||||
@@ -122,23 +122,28 @@
|
||||
</view>
|
||||
|
||||
<!-- 4. 新增:简历上传区域(固定在页面底部) -->
|
||||
<!-- <view class="resume-upload-section">
|
||||
<view class="resume-upload-section">
|
||||
<!-- 上传按钮 -->
|
||||
<button class="upload-btn" @click="handleResumeUpload" :loading="isUploading" :disabled="isUploading">
|
||||
<uni-icons type="cloud-upload" size="20"></uni-icons>
|
||||
<!-- <image class="upload-icon" src="/static/icons/upload-file.png" mode="widthFix"></image> -->
|
||||
<text class="upload-text">
|
||||
{{ uploadedResumeName || '上传简历' }}
|
||||
</text>
|
||||
<!-- 已上传时显示“重新上传”文字 -->
|
||||
<text class="reupload-text" v-if="uploadedResumeName">(重新上传)</text>
|
||||
</button>
|
||||
|
||||
<!-- 上传说明 -->
|
||||
<text class="upload-tip">支持 PDF、Word 格式,文件大小不超过 20MB</text>
|
||||
|
||||
<!-- 已上传文件信息(可选) -->
|
||||
<view class="uploaded-file-info" v-if="uploadedResumeName">
|
||||
<image class="file-icon" src="/static/icons/file-icon.png" mode="widthFix"></image>
|
||||
<text class="file-name">{{ uploadedResumeName }}</text>
|
||||
<button class="delete-file-btn" size="mini" @click.stop="handleDeleteResume">删除</button>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -16,30 +16,28 @@
|
||||
</template>
|
||||
<view class="content" v-show="!isEmptyObject(jobInfo)">
|
||||
<view class="content-top btn-feel">
|
||||
<view style="background: #ffffff;padding: 24rpx;box-shadow: 0rpx 0rpx 8rpx 0rpx rgba(0,0,0,0.04);border-radius: 20rpx 20rpx 20rpx 20rpx;position: relative;overflow: hidden;">
|
||||
<view class="top-salary">
|
||||
<Salary-Expectation
|
||||
:max-salary="jobInfo.maxSalary"
|
||||
:min-salary="jobInfo.minSalary"
|
||||
:is-month="true"
|
||||
></Salary-Expectation>
|
||||
<view class="top-salary">
|
||||
<Salary-Expectation
|
||||
:max-salary="jobInfo.maxSalary"
|
||||
:min-salary="jobInfo.minSalary"
|
||||
:is-month="true"
|
||||
></Salary-Expectation>
|
||||
</view>
|
||||
<view class="top-name">{{ jobInfo.jobTitle }}</view>
|
||||
<view class="top-info">
|
||||
<view class="info-img"><image src="/static/icon/post12.png"></image></view>
|
||||
<view class="info-text">
|
||||
<dict-Label dictType="experience" :value="jobInfo.experience"></dict-Label>
|
||||
</view>
|
||||
<view class="top-name">{{ jobInfo.jobTitle }}</view>
|
||||
<view class="top-info">
|
||||
<view class="info-img"><image src="/static/icon/post12.png"></image></view>
|
||||
<view class="info-text">
|
||||
<dict-Label dictType="experience" :value="jobInfo.experience"></dict-Label>
|
||||
</view>
|
||||
<view class="info-img mar_le20"><image src="/static/icon/post13.png"></image></view>
|
||||
<view class="info-text">
|
||||
<dict-Label dictType="education" :value="jobInfo.education"></dict-Label>
|
||||
</view>
|
||||
</view>
|
||||
<view class="position-source">
|
||||
<text>来源 </text>
|
||||
{{ jobInfo.dataSource }}
|
||||
<view class="info-img mar_le20"><image src="/static/icon/post13.png"></image></view>
|
||||
<view class="info-text">
|
||||
<dict-Label dictType="education" :value="jobInfo.education"></dict-Label>
|
||||
</view>
|
||||
</view>
|
||||
<view class="position-source">
|
||||
<text>来源 </text>
|
||||
{{ jobInfo.dataSource }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="ai-explain" v-if="jobInfo.isExplain">
|
||||
<view class="exbg">
|
||||
@@ -100,7 +98,7 @@
|
||||
></map>
|
||||
</view>
|
||||
</view>
|
||||
<view class="content-card" v-if="currentUserType !== 0">
|
||||
<view class="content-card" v-if="!userInfo.isCompanyUser">
|
||||
<view class="card-title">
|
||||
<text class="title">竞争力分析</text>
|
||||
</view>
|
||||
@@ -193,12 +191,6 @@ import RadarMap from './component/radarMap.vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import useUserStore from '@/stores/useUserStore';
|
||||
const { userInfo } = storeToRefs(useUserStore());
|
||||
// 与首页一致的用户类型获取:优先store,兜底缓存
|
||||
const currentUserType = computed(() => {
|
||||
const storeIsCompanyUser = userInfo.value?.isCompanyUser;
|
||||
const cachedIsCompanyUser = (uni.getStorageSync('userInfo') || {}).isCompanyUser;
|
||||
return Number(storeIsCompanyUser !== undefined ? storeIsCompanyUser : cachedIsCompanyUser);
|
||||
});
|
||||
const { $api, navTo, getLenPx, parseQueryParams, navBack, isEmptyObject } = inject('globalFunction');
|
||||
import config from '@/config.js';
|
||||
const matchingDegree = ref(['一般', '良好', '优秀', '极好']);
|
||||
@@ -271,11 +263,11 @@ function seeExplain() {
|
||||
function getDetail(jobId) {
|
||||
return new Promise((reslove, reject) => {
|
||||
$api.createRequest(`/app/job/${jobId}`).then((resData) => {
|
||||
const { latitude, longitude, companyName, companyId } = resData.data;
|
||||
const { latitude, longitude, companyName, companyId, isCompanyUser } = resData.data;
|
||||
jobInfo.value = resData.data;
|
||||
reslove(resData.data);
|
||||
getCompanyIsAJobs(companyId);
|
||||
if (currentUserType.value !== 0) {
|
||||
if (isCompanyUser) {
|
||||
getCompetivetuveness(jobId);
|
||||
}
|
||||
// getCompetivetuveness(jobId);
|
||||
@@ -511,11 +503,11 @@ for i in 0..100
|
||||
.content{
|
||||
padding: 0 28rpx
|
||||
height: 100%
|
||||
padding-top: 28rpx
|
||||
.content-top{
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0rpx 0rpx 8rpx 0rpx rgba(0,0,0,0.04);
|
||||
border-radius: 20rpx 20rpx 20rpx 20rpx;
|
||||
padding: 24rpx
|
||||
padding: 52rpx 32rpx 34rpx 32rpx
|
||||
position: relative
|
||||
overflow: hidden
|
||||
.top-salary{
|
||||
|
||||
@@ -1,559 +0,0 @@
|
||||
<template>
|
||||
<AppLayout title="" :use-scroll-view="false">
|
||||
<template #headerleft>
|
||||
<view class="btnback">
|
||||
<image src="@/static/icon/back.png" @click="navBack"></image>
|
||||
</view>
|
||||
</template>
|
||||
<view class="content">
|
||||
<view class="content-top">
|
||||
<view class="companyinfo-left">
|
||||
<image src="@/static/icon/companyIcon.png" mode=""></image>
|
||||
</view>
|
||||
<view class="companyinfo-right">
|
||||
<view class="row1 line_2">{{ fairInfo?.name }}</view>
|
||||
<view class="row2">
|
||||
<text>{{ fairInfo.location }}</text>
|
||||
<convert-distance
|
||||
:alat="fairInfo.latitude"
|
||||
:along="fairInfo.longitude"
|
||||
:blat="latitudeVal"
|
||||
:blong="longitudeVal"
|
||||
></convert-distance>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="locations">
|
||||
<image class="location-img" src="/static/icon/mapLine.png"></image>
|
||||
<view class="location-info">
|
||||
<view class="info">
|
||||
<text class="info-title">{{ fairInfo.address }}</text>
|
||||
<text class="info-text">位置</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="conetent-info" :class="{ expanded: isExpanded }">
|
||||
<view class="info-title">内容描述</view>
|
||||
<view class="info-desirption">{{ fairInfo.description }}</view>
|
||||
<!-- <view class="info-title title2">公司地址</view>
|
||||
<view class="locationCompany"></view> -->
|
||||
<view class="company-times">
|
||||
<view class="info-title">内容描述</view>
|
||||
<view class="card-times">
|
||||
<view class="time-left">
|
||||
<view class="left-date">{{ parseDateTime(fairInfo.startTime).time }}</view>
|
||||
<view class="left-dateDay">{{ parseDateTime(fairInfo.startTime).date }}</view>
|
||||
</view>
|
||||
<view class="line"></view>
|
||||
<view class="time-center">
|
||||
<view class="center-date">
|
||||
{{ getTimeStatus(fairInfo.startTime, fairInfo.endTime).statusText }}
|
||||
</view>
|
||||
<view class="center-dateDay">
|
||||
{{ getHoursBetween(fairInfo.startTime, fairInfo.endTime) }}小时
|
||||
</view>
|
||||
</view>
|
||||
<view class="line"></view>
|
||||
<view class="time-right">
|
||||
<view class="left-date">{{ parseDateTime(fairInfo.endTime).time }}</view>
|
||||
<view class="left-dateDay">{{ parseDateTime(fairInfo.endTime).date }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="expand" @click="expand">
|
||||
<text>{{ isExpanded ? '收起' : '展开' }}</text>
|
||||
<image
|
||||
class="expand-img"
|
||||
:class="{ 'expand-img-active': !isExpanded }"
|
||||
src="@/static/icon/downs.png"
|
||||
></image>
|
||||
</view>
|
||||
<scroll-view scroll-y class="Detailscroll-view">
|
||||
<view class="views">
|
||||
<view class="Detail-title">
|
||||
<text class="title">参会单位({{ companyList.length }})</text>
|
||||
</view>
|
||||
<renderCompanys
|
||||
v-if="companyList.length"
|
||||
:list="companyList"
|
||||
:longitude="longitudeVal"
|
||||
:latitude="latitudeVal"
|
||||
></renderCompanys>
|
||||
<empty v-else pdTop="200"></empty>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<template #footer>
|
||||
<view class="footer" v-if="hasnext">
|
||||
<view
|
||||
class="btn-wq button-click"
|
||||
:class="{ 'btn-desbel': fairInfo.isCollection }"
|
||||
@click="applyExhibitors"
|
||||
>
|
||||
{{ fairInfo.isCollection ? '已预约招聘会' : '预约招聘会' }}
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</AppLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import point from '@/static/icon/point.png';
|
||||
import { reactive, inject, watch, ref, onMounted, computed } from 'vue';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import dictLabel from '@/components/dict-Label/dict-Label.vue';
|
||||
import useLocationStore from '@/stores/useLocationStore';
|
||||
const { $api, navTo, vacanciesTo, navBack } = inject('globalFunction');
|
||||
import { storeToRefs } from 'pinia';
|
||||
const { longitudeVal, latitudeVal } = storeToRefs(useLocationStore());
|
||||
|
||||
const isExpanded = ref(false);
|
||||
const fairInfo = ref({});
|
||||
const companyList = ref([]);
|
||||
const hasnext = ref(true);
|
||||
onLoad((options) => {
|
||||
getCompanyInfo(options.jobFairId);
|
||||
});
|
||||
|
||||
function getCompanyInfo(id) {
|
||||
$api.createRequest(`/app/fair/${id}`).then((resData) => {
|
||||
fairInfo.value = resData.data;
|
||||
companyList.value = resData.data.companyList;
|
||||
hasAppointment();
|
||||
});
|
||||
}
|
||||
|
||||
const hasAppointment = () => {
|
||||
const isTimePassed = (timeStr) => {
|
||||
const targetTime = new Date(timeStr.replace(/-/g, '/')).getTime(); // 兼容格式
|
||||
const now = Date.now();
|
||||
return now < targetTime;
|
||||
};
|
||||
|
||||
hasnext.value = isTimePassed(fairInfo.value.startTime);
|
||||
};
|
||||
|
||||
function openMap(lat, lng, name = '位置') {
|
||||
const isConfirmed = window.confirm('是否打开地图查看位置?');
|
||||
if (!isConfirmed) return;
|
||||
|
||||
// 使用高德地图或百度地图的 H5 链接打开
|
||||
const url = `https://uri.amap.com/marker?position=${lng},${lat}&name=${encodeURIComponent(name)}`;
|
||||
window.location.href = url;
|
||||
}
|
||||
|
||||
function expand() {
|
||||
isExpanded.value = !isExpanded.value;
|
||||
}
|
||||
|
||||
// 取消/收藏岗位
|
||||
function applyExhibitors() {
|
||||
const fairId = fairInfo.value.jobFairId;
|
||||
if (fairInfo.value.isCollection) {
|
||||
// $api.createRequest(`/app/fair/collection/${fairId}`, {}, 'DELETE').then((resData) => {
|
||||
// getCompanyInfo(fairId);
|
||||
// $api.msg('取消预约成功');
|
||||
// });
|
||||
$api.msg('已预约成功');
|
||||
} else {
|
||||
$api.createRequest(`/app/fair/collection/${fairId}`, {}, 'POST').then((resData) => {
|
||||
getCompanyInfo(fairId);
|
||||
$api.msg('预约成功');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function toIOSDate(input) {
|
||||
if (!input) return null;
|
||||
if (input instanceof Date) return isNaN(input.getTime()) ? null : input;
|
||||
if (typeof input === 'number') {
|
||||
const d = new Date(input);
|
||||
return isNaN(d.getTime()) ? null : d;
|
||||
}
|
||||
if (typeof input !== 'string') return null;
|
||||
let s = input.trim();
|
||||
if (/^\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}(:\d{2})?$/.test(s)) {
|
||||
s = s.replace(' ', 'T');
|
||||
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/.test(s)) {
|
||||
s = s + ':00';
|
||||
}
|
||||
}
|
||||
const d = new Date(s);
|
||||
return isNaN(d.getTime()) ? null : d;
|
||||
}
|
||||
|
||||
function parseDateTime(datetimeStr) {
|
||||
if (!datetimeStr) return { time: '', date: '' };
|
||||
|
||||
const dateObj = toIOSDate(datetimeStr);
|
||||
|
||||
if (isNaN(dateObj.getTime())) return { time: '', date: '' }; // 无效时间
|
||||
|
||||
const year = dateObj.getFullYear();
|
||||
const month = String(dateObj.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(dateObj.getDate()).padStart(2, '0');
|
||||
const hours = String(dateObj.getHours()).padStart(2, '0');
|
||||
const minutes = String(dateObj.getMinutes()).padStart(2, '0');
|
||||
|
||||
return {
|
||||
time: `${hours}:${minutes}`,
|
||||
date: `${year}年${month}月${day}日`,
|
||||
};
|
||||
}
|
||||
|
||||
function getTimeStatus(startTimeStr, endTimeStr) {
|
||||
const now = new Date();
|
||||
const startTime = toIOSDate(startTimeStr);
|
||||
const endTime = toIOSDate(endTimeStr);
|
||||
|
||||
if (!startTime || !endTime) {
|
||||
return { status: 1, statusText: '时间异常' };
|
||||
}
|
||||
|
||||
let status = 0;
|
||||
let statusText = '开始中';
|
||||
if (now < startTime) {
|
||||
status = 2;
|
||||
statusText = '待开始';
|
||||
} else if (now > endTime) {
|
||||
status = 1;
|
||||
statusText = '已过期';
|
||||
} else {
|
||||
status = 0;
|
||||
statusText = '进行中';
|
||||
}
|
||||
return { status, statusText };
|
||||
}
|
||||
|
||||
function getHoursBetween(startTimeStr, endTimeStr) {
|
||||
const start = toIOSDate(startTimeStr);
|
||||
const end = toIOSDate(endTimeStr);
|
||||
if (!start || !end) return 0;
|
||||
const diffMs = end - start;
|
||||
const diffHours = diffMs / (1000 * 60 * 60);
|
||||
return +diffHours.toFixed(2);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.btnback{
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
}
|
||||
.btn {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
}
|
||||
image {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.content{
|
||||
height: 100%
|
||||
display: flex;
|
||||
flex-direction: column
|
||||
.content-top{
|
||||
padding: 28rpx
|
||||
padding-top: 50rpx
|
||||
display: flex
|
||||
flex-direction: row
|
||||
flex-wrap: nowrap
|
||||
.companyinfo-left{
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
margin-right: 24rpx
|
||||
}
|
||||
.companyinfo-right{
|
||||
flex: 1
|
||||
.row1{
|
||||
font-weight: 500;
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
font-family: 'PingFangSC-Medium', 'PingFang SC', 'Helvetica Neue', Helvetica, Arial, 'Microsoft YaHei', sans-serif;
|
||||
}
|
||||
.row2{
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #6C7282;
|
||||
line-height: 45rpx;
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
}
|
||||
}
|
||||
}
|
||||
.locations{
|
||||
padding: 0 28rpx
|
||||
height: 86rpx;
|
||||
position: relative
|
||||
margin-bottom: 36rpx
|
||||
.location-img{
|
||||
border-radius: 8rpx 8rpx 8rpx 8rpx;
|
||||
border: 2rpx solid #EFEFEF;
|
||||
}
|
||||
.location-info{
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
width: 100%
|
||||
height: 100%
|
||||
|
||||
.info{
|
||||
padding: 0 60rpx
|
||||
height: 100%
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: space-between
|
||||
white-space: nowrap
|
||||
padding-top: rpx
|
||||
.info-title{
|
||||
font-weight: 600;
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
}
|
||||
.info-text{
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #9B9B9B;
|
||||
position: relative;
|
||||
padding-right: 20rpx
|
||||
|
||||
}
|
||||
.info-text::before{
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
content: '';
|
||||
width: 4rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 2rpx
|
||||
background: #9B9B9B;
|
||||
transform: translate(0, -75%) rotate(-45deg)
|
||||
}
|
||||
|
||||
.info-text::after {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
content: '';
|
||||
width: 4rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 2rpx
|
||||
background: #9B9B9B;
|
||||
transform: translate(0, -25%) rotate(45deg)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
.conetent-info{
|
||||
padding: 0 28rpx
|
||||
overflow: hidden;
|
||||
max-height: 0rpx;
|
||||
transition: max-height 0.3s ease;
|
||||
.info-title{
|
||||
font-weight: 600;
|
||||
font-size: 28rpx;
|
||||
color: #000000;
|
||||
}
|
||||
.info-desirption{
|
||||
margin-top: 12rpx
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #495265;
|
||||
text-align: justified;
|
||||
}
|
||||
.title2{
|
||||
margin-top: 48rpx
|
||||
}
|
||||
}
|
||||
.company-times{
|
||||
padding-top: 40rpx
|
||||
.info-title{
|
||||
font-weight: 600;
|
||||
font-size: 28rpx;
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
.expanded {
|
||||
max-height: 1000rpx; // 足够显示完整内容
|
||||
}
|
||||
.expand{
|
||||
display: flex
|
||||
flex-wrap: nowrap
|
||||
white-space: nowrap
|
||||
justify-content: center
|
||||
margin-bottom: 46rpx
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #256BFA;
|
||||
.expand-img{
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
.expand-img-active{
|
||||
transform: rotate(180deg)
|
||||
}
|
||||
}
|
||||
}
|
||||
.Detailscroll-view{
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
background: #F4F4F4;
|
||||
.views{
|
||||
padding: 28rpx
|
||||
.Detail-title{
|
||||
font-weight: 600;
|
||||
font-size: 32rpx;
|
||||
color: #000000;
|
||||
position: relative;
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
.title{
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
.Detail-title::before{
|
||||
position: absolute
|
||||
content: '';
|
||||
left: -14rpx
|
||||
bottom: 0
|
||||
height: 16rpx;
|
||||
width: 108rpx;
|
||||
background: linear-gradient(to right, #CBDEFF, #FFFFFF);
|
||||
border-radius: 8rpx;
|
||||
z-index: 1;
|
||||
}
|
||||
.cards{
|
||||
padding: 32rpx;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0rpx 0rpx 8rpx 0rpx rgba(0,0,0,0.04);
|
||||
border-radius: 20rpx 20rpx 20rpx 20rpx;
|
||||
margin-top: 22rpx;
|
||||
.card-company{
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
align-items: flex-start
|
||||
.company{
|
||||
font-weight: 500;
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
}
|
||||
.salary{
|
||||
font-weight: 500;
|
||||
font-size: 28rpx;
|
||||
color: #4C6EFB;
|
||||
white-space: nowrap
|
||||
line-height: 48rpx
|
||||
}
|
||||
}
|
||||
.card-companyName{
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #6C7282;
|
||||
}
|
||||
.card-tags{
|
||||
display: flex
|
||||
flex-wrap: wrap
|
||||
.tag{
|
||||
width: fit-content;
|
||||
height: 30rpx;
|
||||
background: #F4F4F4;
|
||||
border-radius: 4rpx;
|
||||
padding: 6rpx 20rpx;
|
||||
line-height: 30rpx;
|
||||
font-weight: 400;
|
||||
font-size: 24rpx;
|
||||
color: #6C7282;
|
||||
text-align: center;
|
||||
margin-top: 14rpx;
|
||||
white-space: nowrap
|
||||
margin-right: 20rpx
|
||||
}
|
||||
}
|
||||
.card-bottom{
|
||||
margin-top: 32rpx
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
font-size: 28rpx;
|
||||
color: #6C7282;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.card-times{
|
||||
display: flex;
|
||||
justify-content: space-between
|
||||
align-items: center
|
||||
padding: 24rpx 30rpx 10rpx 30rpx
|
||||
.time-left,
|
||||
.time-right{
|
||||
text-align: center
|
||||
.left-date{
|
||||
font-weight: 500;
|
||||
font-size: 48rpx;
|
||||
color: #333333;
|
||||
}
|
||||
.left-dateDay{
|
||||
font-weight: 400;
|
||||
font-size: 24rpx;
|
||||
color: #333333;
|
||||
margin-top: 12rpx
|
||||
}
|
||||
}
|
||||
.line{
|
||||
width: 40rpx;
|
||||
height: 0rpx;
|
||||
border: 2rpx solid #D4D4D4;
|
||||
margin-top: 64rpx
|
||||
}
|
||||
.time-center{
|
||||
text-align: center;
|
||||
display: flex
|
||||
flex-direction: column
|
||||
justify-content: center
|
||||
align-items: center
|
||||
.center-date{
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #FF881A;
|
||||
padding-top: 10rpx
|
||||
}
|
||||
.center-dateDay{
|
||||
font-weight: 400;
|
||||
font-size: 24rpx;
|
||||
color: #333333;
|
||||
margin-top: 6rpx
|
||||
line-height: 48rpx;
|
||||
width: 104rpx;
|
||||
height: 48rpx;
|
||||
background: #F9F9F9;
|
||||
border-radius: 8rpx 8rpx 8rpx 8rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.footer{
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0rpx -4rpx 24rpx 0rpx rgba(11,44,112,0.12);
|
||||
border-radius: 0rpx 0rpx 0rpx 0rpx;
|
||||
padding: 40rpx 28rpx 20rpx 28rpx
|
||||
.btn-wq{
|
||||
height: 90rpx;
|
||||
background: #256BFA;
|
||||
border-radius: 12rpx 12rpx 12rpx 12rpx;
|
||||
font-weight: 500;
|
||||
font-size: 32rpx;
|
||||
color: #FFFFFF;
|
||||
text-align: center;
|
||||
line-height: 90rpx
|
||||
}
|
||||
.btn-desbel{
|
||||
background: #6697FB;
|
||||
box-shadow: 0rpx -4rpx 24rpx 0rpx rgba(11,44,112,0.12);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,237 +0,0 @@
|
||||
<template>
|
||||
<AppLayout title="" :use-scroll-view="false">
|
||||
<view class="wrap">
|
||||
<view class="login_index">
|
||||
<input class="input" placeholder="请输入账号" placeholder-class="inputplace" v-model="form.username" />
|
||||
<view class="login_yzm">
|
||||
<input class="input" type="password" placeholder="请输入密码" placeholder-class="inputplace"
|
||||
v-model="form.password" />
|
||||
</view>
|
||||
<view class="login_yzm">
|
||||
<input class="input" placeholder="请输入验证码" placeholder-class="inputplace" v-model="form.code" />
|
||||
<image class="yzm" :src="codeUrl" @click="getCodeImg"></image>
|
||||
</view>
|
||||
|
||||
<button class="com-btn" @click="register">登 录</button>
|
||||
</view>
|
||||
</view>
|
||||
</AppLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
reactive,
|
||||
inject,
|
||||
watch,
|
||||
ref,
|
||||
onMounted,
|
||||
onUnmounted
|
||||
} from 'vue'
|
||||
import {
|
||||
onLoad,
|
||||
onShow
|
||||
} from '@dcloudio/uni-app';
|
||||
const {
|
||||
$api,
|
||||
navTo,
|
||||
vacanciesTo,
|
||||
navBack
|
||||
} = inject("globalFunction");
|
||||
const placeholderStyle = 'font-size:30rpx'
|
||||
const checked = ref(true)
|
||||
const codeUrl = ref('')
|
||||
|
||||
const form = reactive({
|
||||
username: 'langchaojituan',
|
||||
password: 'Aa123456?',
|
||||
rememberMe: false,
|
||||
code: '',
|
||||
uuid: ''
|
||||
})
|
||||
|
||||
onLoad(() => {
|
||||
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getCodeImg()
|
||||
})
|
||||
|
||||
function register() {
|
||||
if (!form.username) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '请输入用户名'
|
||||
})
|
||||
return
|
||||
}
|
||||
if (!form.password) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '请输入密码'
|
||||
})
|
||||
return
|
||||
}
|
||||
if (!form.uuid) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '请输入验证码'
|
||||
})
|
||||
return
|
||||
}
|
||||
uni.showLoading({
|
||||
title: '登录中...',
|
||||
mask: true
|
||||
})
|
||||
$api.myRequest('/auth/login',form,'post',10100).then((res) => {
|
||||
console.log(res, 'res')
|
||||
uni.setStorageSync('Padmin-Token', res.data.access_token)
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
codeUrl.value = 'data:image/gif;base64,' + res.img
|
||||
}).catch(() => {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '登录失败,请重试'
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function getCodeImg() {
|
||||
$api.myRequest('/code',{},'get',10100).then((resData) => {
|
||||
codeUrl.value = 'data:image/gif;base64,' + resData.img
|
||||
form.uuid = resData.uuid
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="stylus">
|
||||
.wrap {
|
||||
background-color: #ffffff;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
|
||||
.lg-head {
|
||||
height: 480rpx;
|
||||
background: #46ca98;
|
||||
position: relative;
|
||||
|
||||
.view_logo {
|
||||
text-align: center;
|
||||
|
||||
.login_logo {
|
||||
width: 300rpx;
|
||||
height: 300rpx;
|
||||
margin-top: 100rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.bg-cover {
|
||||
position: absolute;
|
||||
bottom: -4rpx;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 30rpx;
|
||||
background-size: 100% 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.login_index {
|
||||
font-size: 36rpx;
|
||||
font-weight: 500;
|
||||
width: 596rpx;
|
||||
margin: 0 auto;
|
||||
|
||||
::v-deep .is-input-border {
|
||||
border: 0;
|
||||
border-bottom: 1px solid #dcdfe6 !important;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
::v-deep .uni-input-input {
|
||||
font-size: 32rpx;
|
||||
padding-left: 10rpx;
|
||||
}
|
||||
|
||||
::v-deep .uniui-contact-filled:before {
|
||||
color: #46ca98;
|
||||
font-size: 50rpx;
|
||||
}
|
||||
|
||||
::v-deep .uniui-locked-filled:before {
|
||||
color: #46ca98;
|
||||
font-size: 50rpx;
|
||||
}
|
||||
|
||||
.login_yzm {
|
||||
margin-top: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.yzm {
|
||||
width: 200rpx;
|
||||
height: 80rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.com-btn {
|
||||
height: 100rpx;
|
||||
background: #46ca98;
|
||||
border-radius: 50rpx;
|
||||
color: #fff;
|
||||
margin-top: 100rpx;
|
||||
}
|
||||
|
||||
.login_wt {
|
||||
margin: 0 auto;
|
||||
text-align: right;
|
||||
font-size: 24rpx;
|
||||
color: rgba(134, 134, 136, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.lg-bottom {
|
||||
position: absolute;
|
||||
bottom: -3px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
|
||||
.bottom-svg {
|
||||
position: absolute;
|
||||
bottom: -3px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.login_tongyi {
|
||||
|
||||
font-size: 26rpx;
|
||||
color: rgba(196, 196, 196, 1);
|
||||
width: 620rpx;
|
||||
margin: 32rpx auto;
|
||||
text-align: center;
|
||||
|
||||
text {
|
||||
color: rgba(86, 176, 236, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.input {
|
||||
padding: 0 30rpx 0 80rpx;
|
||||
height: 80rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 75rpx 75rpx 75rpx 75rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.inputplace {
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #B5B5B5;
|
||||
}
|
||||
</style>
|
||||
@@ -14,7 +14,8 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="u-menu-wrap" :style="{height: winHeight - barHeight - loginHeight + 'px'}">
|
||||
<!-- :style="{height: winHeight - barHeight - loginHeight + 'px'}" -->
|
||||
<view class="u-menu-wrap" >
|
||||
<scroll-view scroll-y scroll-with-animation class="u-tab-view menu-scroll-view" :scroll-top="scrollTop" :scroll-into-view="itemId">
|
||||
<view v-for="(item,index) in jobList" :key="index" class="u-tab-item"
|
||||
:class="[current == index ? 'u-tab-item-active' : '']" @tap.stop="swichMenu(index)">
|
||||
@@ -275,9 +276,10 @@
|
||||
.jobIndex {
|
||||
position: relative;
|
||||
padding-top: 0;
|
||||
height: 100vh;
|
||||
.u-menu-wrap {
|
||||
display: flex;
|
||||
// height: calc(100vh - 100rpx);
|
||||
height: calc(100vh - 100rpx);
|
||||
overflow: auto;
|
||||
padding-top: 100rpx;
|
||||
.u-tab-view {
|
||||
@@ -301,7 +303,7 @@
|
||||
.u-tab-item-active {
|
||||
position: relative;
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
background: #1677ff;
|
||||
border-radius:5px;
|
||||
@@ -442,6 +444,7 @@
|
||||
overflow: auto;
|
||||
box-shadow: 0 0 4rpx 2rpx #f2f2f2;
|
||||
border-radius: 8rpx;
|
||||
z-index: 90;
|
||||
.link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -28,41 +28,32 @@
|
||||
<text class="title">个人档案</text>
|
||||
</view> -->
|
||||
</view>
|
||||
<view class="head-title">规划中心</view>
|
||||
<view class="head-title">职业生涯规划</view>
|
||||
<view class="nav-block">
|
||||
<view class="item" @click="navDetail(6)">
|
||||
<text class="icon icon-105"></text>
|
||||
<text class="title">生涯罗盘</text>
|
||||
</view>
|
||||
<view class="item" @click="navDetail(2)">
|
||||
<view class="item" @click="navDetail(5)">
|
||||
<text class="icon icon-106"></text>
|
||||
<text class="title">职业生涯路径</text>
|
||||
</view>
|
||||
<view class="item" @click="navDetail(7)">
|
||||
<text class="icon icon-107"></text>
|
||||
<text class="title">学业规划</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="head-title">档案中心</view>
|
||||
<view class="nav-block">
|
||||
</view>
|
||||
<view class="item" @click="navDetail(4)">
|
||||
<text class="icon icon-108"></text>
|
||||
<text class="title">生涯档案</text>
|
||||
</view>
|
||||
<!-- <view class="item" @click="navDetail(5)">
|
||||
<text class="icon icon-11"></text>
|
||||
<text class="title">测试完善个人信息</text>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api1 from "@/apiB/weChartUser.js"
|
||||
import api3 from "@/apiB/tenpayOrder.js"
|
||||
import api4 from "@/apiB/user.js"
|
||||
import api from "@/apiB/user.js"
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
@@ -75,7 +66,6 @@
|
||||
if(user == undefined){
|
||||
this.queryWechartToken();
|
||||
}
|
||||
this.getUserInfor();
|
||||
},
|
||||
methods: {
|
||||
// 返回
|
||||
@@ -83,7 +73,7 @@
|
||||
uni.navigateBack(-1);
|
||||
},
|
||||
async getUserInfor(){
|
||||
const res = await api4.getUserBasisInfo();
|
||||
const res = await api.getUserBasisInfo();
|
||||
if (res.Result == 1) {
|
||||
const data = res.Data.data;
|
||||
if(!data.SpecialtyName){
|
||||
@@ -129,7 +119,7 @@
|
||||
}
|
||||
case 5 : {
|
||||
uni.navigateTo({
|
||||
url: "/packageB/pages/userCenter/fillInInformation"
|
||||
url: "/packageB/pages/userCenter/professionPath"
|
||||
})
|
||||
break;
|
||||
}
|
||||
@@ -157,8 +147,7 @@
|
||||
provider: 'weixin',
|
||||
success: (res)=> {
|
||||
console.log("res.code======="+res.code);
|
||||
api3.getOpenId(res.code).then((res2) => {
|
||||
console.log("88888888");
|
||||
api.getOpenId(res.code).then((res2) => {
|
||||
// uni.setStorageSync('userInfo', data.Data);
|
||||
const userInfo = res2.Data
|
||||
this.user = userInfo.user;
|
||||
@@ -178,7 +167,7 @@
|
||||
async getAccessTokenAndUser() {
|
||||
const userInfo = await this.loginMpWeixin();
|
||||
return new Promise((resolve,reject)=>{
|
||||
api1.getAccessTokenAndUser(userInfo.openid).then((res)=>{
|
||||
api.getAccessTokenAndUser(userInfo.openid).then((res)=>{
|
||||
if(res.Result == 1){
|
||||
this.user = res.Data.User;
|
||||
userInfo.token = res.Data.Token;
|
||||
@@ -199,11 +188,12 @@
|
||||
title: "加载中"
|
||||
})
|
||||
const data = await this.getAccessTokenAndUser();
|
||||
api1.queryWechartToken(data.user.Id,data.user.SchoolId,data.user.UserType).then((res)=>{
|
||||
api.queryWechartToken(data.user.Id,data.user.SchoolId,data.user.UserType).then((res)=>{
|
||||
uni.hideLoading();
|
||||
if(res.Result == 1){
|
||||
data.userToken = res.Data.token;
|
||||
uni.setStorageSync('userInfo',data);
|
||||
this.getUserInfor();
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -121,11 +121,7 @@
|
||||
recordId: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -134,12 +130,12 @@
|
||||
showTime: false, //显示时间选择
|
||||
treeList: [], //时间列表
|
||||
childrenList: [],//时间子级
|
||||
selYTItem1: null,//临时选中的学年1
|
||||
selRepItem1: null,//临时选中的报告1
|
||||
selYTItem2: null,//临时选中的学年2
|
||||
selRepItem2: null,//临时选中的报告2
|
||||
checkedRepItem1: null,//确认选中报告1
|
||||
checkedRepItem2: null,//确认选中报告2
|
||||
selYTItem1: {},//临时选中的学年1
|
||||
selRepItem1: {},//临时选中的报告1
|
||||
selYTItem2: {},//临时选中的学年2
|
||||
selRepItem2: {},//临时选中的报告2
|
||||
checkedRepItem1: {},//确认选中报告1
|
||||
checkedRepItem2: {},//确认选中报告2
|
||||
timeType: 1,//时间类型,是报告时间1,还是对比时间2
|
||||
optionStr1: "",//我的报告展示字段
|
||||
optionStr2: "",//我的报告展示字段
|
||||
@@ -240,12 +236,15 @@
|
||||
changeYearTrem(ITEM,INDEX){
|
||||
if(INDEX == 1){
|
||||
this.selYTItem1 = ITEM;
|
||||
this.selRepItem1 = null;
|
||||
this.selRepItem1 = {};
|
||||
}else {
|
||||
this.selYTItem2 = ITEM;
|
||||
this.selRepItem2 = null;
|
||||
this.selRepItem2 = {};
|
||||
}
|
||||
this.childrenList = ITEM.children;
|
||||
console.log(this.selYTItem2);
|
||||
console.log(this.selRepItem2);
|
||||
console.log(this.childrenList);
|
||||
},
|
||||
// 显示团队对比弹窗
|
||||
showContrastLayer(){
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<text>我的报告</text>
|
||||
</view>
|
||||
<view class="content">
|
||||
<contrastBox :value="isShowEchart" @updateValue="handleChildValueChange" :testType="testType" :userId="userId" :recordId="recordId" @compareParameters="opCompareParameters"></contrastBox>
|
||||
<contrastBox @updateValue="handleChildValueChange" :testType="testType" :userId="userId" :recordId="recordId" @compareParameters="opCompareParameters"></contrastBox>
|
||||
<view class="section-block">
|
||||
<testHead :reportTitle="'通用(职业)能力测评报告'" :testTime="testDate" :isIntroduce="false" :isVideo="false" :videoUrl="videoUrl" :introduceUrl="introduceUrl" ></testHead>
|
||||
<view class="s-line"></view>
|
||||
@@ -39,7 +39,7 @@
|
||||
<view class="code-value">
|
||||
<text class="scort" v-text="lowStr==''?'无':lowStr"></text>
|
||||
</view>
|
||||
<view v-show="isShowEchart" class="chat-wrap">
|
||||
<view class="chat-wrap">
|
||||
<view class="row-item"><l-echart ref="chartRef" class="charts-box"></l-echart>
|
||||
</view>
|
||||
</view>
|
||||
@@ -155,7 +155,6 @@
|
||||
recordId: 0,
|
||||
labelName1: "",//表值1
|
||||
labelName2: "",//表值2
|
||||
isShowEchart:true,//是否显示图表
|
||||
}
|
||||
},
|
||||
onLoad(e) {
|
||||
@@ -172,10 +171,6 @@
|
||||
this.getTestRecord(this.currentYear, this.currentTerm,this.recordId);
|
||||
},
|
||||
methods: {
|
||||
handleChildValueChange(newValue) {
|
||||
console.log("66666====");
|
||||
this.isShowEchart = newValue
|
||||
},
|
||||
changeMore(INDEX) {
|
||||
this.list[INDEX].showMore = !this.list[INDEX].showMore
|
||||
this.$forceUpdate();
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
<text>我的报告</text>
|
||||
</view>
|
||||
<view class="content">
|
||||
<contrastBox :value="isShowEchart" @updateValue="handleChildValueChange" :testType="testType" :userId="userId" :recordId="recordId" @compareParameters="opCompareParameters"></contrastBox>
|
||||
<contrastBox :testType="testType" :userId="userId" :recordId="recordId" @compareParameters="opCompareParameters"></contrastBox>
|
||||
<view class="section-block">
|
||||
<testHead :value="isShowEchart" @updateValue="handleChildValueChange" :reportTitle="'职业兴趣测评报告'" :testTime="testDate" :isIntroduce="false" :isVideo="true" :videoUrl="videoUrl" :introduceUrl="introduceUrl" ></testHead>
|
||||
<testHead :reportTitle="'职业兴趣测评报告'" :testTime="testDate" :isIntroduce="false" :isVideo="true" :videoUrl="videoUrl" :introduceUrl="introduceUrl" ></testHead>
|
||||
<view class="s-line"></view>
|
||||
<view class="title">
|
||||
<view class="name">兴趣代码</view>
|
||||
@@ -38,7 +38,7 @@
|
||||
<view class="title">
|
||||
<view class="name">兴趣分数</view>
|
||||
</view>
|
||||
<view v-show="isShowEchart" class="chat-wrap">
|
||||
<view class="chat-wrap">
|
||||
<view class="row-item"><l-echart ref="chartRef" class="charts-box"></l-echart>
|
||||
</view>
|
||||
|
||||
@@ -415,7 +415,6 @@
|
||||
labelName1: "",//表值1
|
||||
labelName2: "",//表值2
|
||||
params: {},//筛选参数
|
||||
isShowEchart:true,//是否显示图表
|
||||
}
|
||||
},
|
||||
onLoad(e) {
|
||||
@@ -445,10 +444,6 @@
|
||||
this.gradeLevel = uni.getStorageSync("userInfo").user.GradeLevel;
|
||||
},
|
||||
methods: {
|
||||
handleChildValueChange(newValue) {
|
||||
console.log("66666====");
|
||||
this.isShowEchart = newValue
|
||||
},
|
||||
showConfirmInfor(){
|
||||
uni.showModal({
|
||||
title:this.layerTitile,
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
<text>我的报告</text>
|
||||
</view>
|
||||
<view class="content">
|
||||
<contrastBox :value="isShowEchart" @updateValue="handleChildValueChange" :testType="testType" :userId="userId" :recordId="recordId" @compareParameters="opCompareParameters"></contrastBox>
|
||||
<contrastBox :testType="testType" :userId="userId" :recordId="recordId" @compareParameters="opCompareParameters"></contrastBox>
|
||||
<view class="section-block">
|
||||
<testHead :value="isShowEchart" @updateValue="handleChildValueChange" :reportTitle="'多元(职业)能力测评报告'" :testTime="testDate" :isIntroduce="false" :isVideo="false" :videoUrl="videoUrl" :introduceUrl="introduceUrl" :introduceUrl2="introduceUrl2"></testHead>
|
||||
<testHead :reportTitle="'多元(职业)能力测评报告'" :testTime="testDate" :isIntroduce="false" :isVideo="false" :videoUrl="videoUrl" :introduceUrl="introduceUrl" :introduceUrl2="introduceUrl2"></testHead>
|
||||
<view class="s-line"></view>
|
||||
<view class="title">
|
||||
<view class="name">能力强</view>
|
||||
@@ -27,42 +27,42 @@
|
||||
<view class="code-value">
|
||||
<text class="scort" v-text="low!=''?low:'无'"></text>
|
||||
</view>
|
||||
<view v-show="isShowEchart" class="chat-wrap blue-bg" style="margin-bottom:68rpx;">
|
||||
<view class="chat-wrap blue-bg" style="margin-bottom:68rpx;">
|
||||
<view class="chat-title">
|
||||
语文相关能力得分
|
||||
</view>
|
||||
<view class="row-item"><l-echart ref="charts1" class="charts-box"></l-echart>
|
||||
</view>
|
||||
</view>
|
||||
<view v-show="isShowEchart" class="chat-wrap purple-bg" style="margin-bottom:68rpx;">
|
||||
<view class="chat-wrap purple-bg" style="margin-bottom:68rpx;">
|
||||
<view class="chat-title">
|
||||
数学相关能力得分
|
||||
</view>
|
||||
<view class="row-item"><l-echart ref="charts2" class="charts-box"></l-echart>
|
||||
</view>
|
||||
</view>
|
||||
<view v-show="isShowEchart" class="chat-wrap orange-bg" style="margin-bottom:68rpx;">
|
||||
<view class="chat-wrap orange-bg" style="margin-bottom:68rpx;">
|
||||
<view class="chat-title">
|
||||
科技相关能力得分
|
||||
</view>
|
||||
<view class="row-item"><l-echart ref="charts3" class="charts-box"></l-echart>
|
||||
</view>
|
||||
</view>
|
||||
<view v-show="isShowEchart" class="chat-wrap green-bg" style="margin-bottom:68rpx;">
|
||||
<view class="chat-wrap green-bg" style="margin-bottom:68rpx;">
|
||||
<view class="chat-title">
|
||||
资讯相关能力得分
|
||||
</view>
|
||||
<view class="row-item"><l-echart ref="charts4" class="charts-box"></l-echart>
|
||||
</view>
|
||||
</view>
|
||||
<view v-show="isShowEchart" class="chat-wrap blue-bg" style="margin-bottom:68rpx;">
|
||||
<view class="chat-wrap blue-bg" style="margin-bottom:68rpx;">
|
||||
<view class="chat-title">
|
||||
人际相关能力得分
|
||||
</view>
|
||||
<view class="row-item"><l-echart ref="charts5" class="charts-box"></l-echart>
|
||||
</view>
|
||||
</view>
|
||||
<view v-show="isShowEchart" class="chat-wrap purple-bg" style="margin-bottom:68rpx;">
|
||||
<view class="chat-wrap purple-bg" style="margin-bottom:68rpx;">
|
||||
<view class="chat-title">
|
||||
美感相关能力得分
|
||||
</view>
|
||||
@@ -70,14 +70,14 @@
|
||||
<view class="row-item"><l-echart ref="charts6" class="charts-box"></l-echart>
|
||||
</view>
|
||||
</view>
|
||||
<view v-show="isShowEchart" class="chat-wrap orange-bg" style="margin-bottom:68rpx;">
|
||||
<view class="chat-wrap orange-bg" style="margin-bottom:68rpx;">
|
||||
<view class="chat-title">
|
||||
自然相关能力得分
|
||||
</view>
|
||||
<view class="row-item"><l-echart ref="charts7" class="charts-box"></l-echart>
|
||||
</view>
|
||||
</view>
|
||||
<view v-show="isShowEchart" class="chat-wrap green-bg">
|
||||
<view class="chat-wrap green-bg">
|
||||
<view class="chat-title">
|
||||
艺能相关能力得分
|
||||
</view>
|
||||
@@ -257,7 +257,6 @@
|
||||
recordId: 0,
|
||||
labelName1: "",//表值1
|
||||
labelName2: "",//表值2
|
||||
isShowEchart:true,
|
||||
}
|
||||
},
|
||||
onLoad(e) {
|
||||
@@ -274,10 +273,6 @@
|
||||
this.getTestRecord(this.currentYear, this.currentTerm,this.recordId);
|
||||
},
|
||||
methods: {
|
||||
handleChildValueChange(newValue) {
|
||||
console.log("66666====");
|
||||
this.isShowEchart = newValue
|
||||
},
|
||||
//对比参数
|
||||
opCompareParameters(value){
|
||||
this.labelName1 = value.name1;
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
<text>我的报告</text>
|
||||
</view>
|
||||
<view class="content">
|
||||
<contrastBox :value="isShowEchart" @updateValue="handleChildValueChange" :testType="testType" :userId="userId" :recordId="recordId" @compareParameters="opCompareParameters"></contrastBox>
|
||||
<contrastBox :testType="testType" :userId="userId" :recordId="recordId" @compareParameters="opCompareParameters"></contrastBox>
|
||||
<view class="section-block">
|
||||
<testHead :value="isShowEchart" @updateValue="handleChildValueChange" :reportTitle="'人格测评报告'" :testTime="testDate" :isIntroduce="false" :isVideo="true" :videoUrl="videoUrl" :introduceUrl="introduceUrl" ></testHead>
|
||||
<testHead :reportTitle="'人格测评报告'" :testTime="testDate" :isIntroduce="false" :isVideo="true" :videoUrl="videoUrl" :introduceUrl="introduceUrl" ></testHead>
|
||||
<view class="s-line"></view>
|
||||
<view class="title">
|
||||
<view class="name">强项特质</view>
|
||||
@@ -146,7 +146,7 @@
|
||||
无弱项特质
|
||||
</view>
|
||||
</view>
|
||||
<view v-show="isShowEchart" class="chat-wrap blue-bg" style="margin-bottom:40rpx;">
|
||||
<view class="chat-wrap blue-bg" style="margin-bottom:40rpx;">
|
||||
<view class="chat-title">
|
||||
综合特质等级侧面图
|
||||
</view>
|
||||
@@ -175,7 +175,7 @@
|
||||
</view>
|
||||
<view class="item-desc">{{scoreSummary1.Desc}}</view>
|
||||
</view>
|
||||
<view v-show="isShowEchart" class="chat-wrap blue-bg">
|
||||
<view class="chat-wrap blue-bg">
|
||||
<view class="chat-title">
|
||||
内外向综合特质侧面图
|
||||
</view>
|
||||
@@ -194,7 +194,7 @@
|
||||
</view>
|
||||
<view class="item-desc">{{scoreSummary2.Desc}}</view>
|
||||
</view>
|
||||
<view v-show="isShowEchart" class="chat-wrap purple-bg">
|
||||
<view class="chat-wrap purple-bg">
|
||||
<view class="chat-title">
|
||||
人际关系综合特质侧面图
|
||||
</view>
|
||||
@@ -213,7 +213,7 @@
|
||||
</view>
|
||||
<view class="item-desc">{{scoreSummary3.Desc}}</view>
|
||||
</view>
|
||||
<view v-show="isShowEchart" class="chat-wrap orange-bg">
|
||||
<view class="chat-wrap orange-bg">
|
||||
<view class="chat-title">
|
||||
严谨性综合特质侧面图
|
||||
</view>
|
||||
@@ -232,7 +232,7 @@
|
||||
</view>
|
||||
<view class="item-desc">{{scoreSummary4.Desc}}</view>
|
||||
</view>
|
||||
<view v-show="isShowEchart" class="chat-wrap green-bg">
|
||||
<view class="chat-wrap green-bg">
|
||||
<view class="chat-title">
|
||||
开放性综合特质侧面图
|
||||
</view>
|
||||
@@ -492,7 +492,6 @@
|
||||
labelName1: "",//表值1
|
||||
labelName2: "",//表值2
|
||||
params: {},//筛选参数
|
||||
isShowEchart:true,//是否显示图表
|
||||
}
|
||||
},
|
||||
onLoad(e) {
|
||||
@@ -508,10 +507,6 @@
|
||||
this.getTestRecord(this.currentYear, this.currentTerm,this.recordId);
|
||||
},
|
||||
methods: {
|
||||
handleChildValueChange(newValue) {
|
||||
console.log("66666====");
|
||||
this.isShowEchart = newValue
|
||||
},
|
||||
//报告介绍
|
||||
playIntroduce() {
|
||||
this.showIntroduce = true;
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
<text>我的报告</text>
|
||||
</view>
|
||||
<view class="content">
|
||||
<contrastBox :value="isShowEchart" @updateValue="handleChildValueChange" :testType="testType" :userId="userId" :recordId="recordId" @compareParameters="opCompareParameters"></contrastBox>
|
||||
<contrastBox :testType="testType" :userId="userId" :recordId="recordId" @compareParameters="opCompareParameters"></contrastBox>
|
||||
<view class="section-block">
|
||||
<testHead :value="isShowEchart" @updateValue="handleChildValueChange" :reportTitle="'工作价值观测评报告'" :testTime="testDate" :isIntroduce="false" :isVideo="true" :videoUrl="videoUrl" :introduceUrl="introduceUrl" ></testHead>
|
||||
<testHead :reportTitle="'工作价值观测评报告'" :testTime="testDate" :isIntroduce="false" :isVideo="true" :videoUrl="videoUrl" :introduceUrl="introduceUrl" ></testHead>
|
||||
<view class="s-line"></view>
|
||||
<view class="title">
|
||||
<view class="name">工作价值类型得分</view>
|
||||
</view>
|
||||
<view v-show="isShowEchart" class="chat-wrap">
|
||||
<view class="chat-wrap">
|
||||
<view class="row-item">
|
||||
<l-echart ref="chartRef" class="charts-box"></l-echart>
|
||||
</view>
|
||||
@@ -21,15 +21,18 @@
|
||||
<view class="name">高分价值观及说明</view>
|
||||
</view>
|
||||
<view class="desc-item-wrap" v-if="tableData.length > 0">
|
||||
<view class="desc-item" v-for="(items,index) in tableData" :key="index">
|
||||
<view class="desc-item" v-show="item.Desc != undefined && item.Desc != ''" v-for="(item,index) in tableData" :key="index">
|
||||
<view class="name-block">
|
||||
<view class="name">{{items.Name}}
|
||||
<view class="name">{{item.Name}}
|
||||
<view class="label">高分</view>
|
||||
</view>
|
||||
<view class="desc">{{items.Desc}}</view>
|
||||
<view class="desc">{{item.Desc}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="desc-item-wrap" v-else>
|
||||
暂无高分价值观
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -135,7 +138,6 @@
|
||||
labelName1: "",//表值1
|
||||
labelName2: "",//表值2
|
||||
params: {},//筛选参数
|
||||
isShowEchart:true,//是否显示图表
|
||||
}
|
||||
},
|
||||
onLoad(e) {
|
||||
@@ -151,13 +153,8 @@
|
||||
this.getTestRecord(this.currentYear, this.currentTerm,this.recordId);
|
||||
},
|
||||
methods: {
|
||||
handleChildValueChange(newValue) {
|
||||
console.log("66666====");
|
||||
this.isShowEchart = newValue
|
||||
},
|
||||
//对比参数
|
||||
opCompareParameters(value){
|
||||
console.log("0000===");
|
||||
this.labelName1 = value.name1;
|
||||
this.labelName2 = value.name2;
|
||||
this.params = value;
|
||||
@@ -199,8 +196,8 @@
|
||||
}
|
||||
})
|
||||
})
|
||||
console.log(scoreList)
|
||||
this.tableData = scoreList;
|
||||
console.log("this.tableData==="+JSON.stringify(this.tableData));
|
||||
this.initChart();
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -79,13 +79,13 @@
|
||||
{{item.JobName}}
|
||||
</view>
|
||||
<view class="td">
|
||||
<text v-for="(ritem,rindex) in item.AbilityPlanList">{{ritem}}</text>
|
||||
<text v-for="(ritem,rindex) in item.AbilityPlanList" :key="rindex">{{ritem}}</text>
|
||||
</view>
|
||||
<view class="td">
|
||||
<text v-for="(ritem,rindex) in item.CoursePlanList">{{ritem.Name}}</text>
|
||||
<text v-for="(ritem,rindex) in item.CoursePlanList" :key="rindex">{{ritem.Name}}</text>
|
||||
</view>
|
||||
<view class="td">
|
||||
<text v-for="(ritem,rindex) in item.JobNameList">{{ritem}}</text>
|
||||
<text v-for="(ritem,rindex) in item.JobNameList" :key="rindex">{{ritem}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="loading" v-if="isLoading">
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api1 from "@/apiB/user.js"
|
||||
import api from "@/apiB/user.js"
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
@@ -309,7 +309,7 @@
|
||||
},
|
||||
methods: {
|
||||
async getUserInfor(){
|
||||
const res = await api1.getUserBasisInfo();
|
||||
const res = await api.getUserBasisInfo();
|
||||
if (res.Result == 1) {
|
||||
const data = res.Data.data;
|
||||
this.specialtyName=data.SpecialtyName;
|
||||
@@ -375,7 +375,7 @@
|
||||
// smsType: 2
|
||||
// }
|
||||
const mobile = this.mobile.replace(/\s+/g, '')
|
||||
const res = await api1.querySendSmsCodeWithoutCode(mobile)
|
||||
const res = await api.querySendSmsCodeWithoutCode(mobile)
|
||||
if (res.Result !== 1) {
|
||||
uni.showToast({
|
||||
title: res.Message,
|
||||
@@ -436,7 +436,7 @@
|
||||
//Phone: this.mobile,
|
||||
//StartYear: this.startYear
|
||||
}
|
||||
const res = await api1.saveUserBasisInfo(mobileCode,data);
|
||||
const res = await api.saveUserBasisInfo(mobileCode,data);
|
||||
uni.hideLoading();
|
||||
if (res.Result == 1) {
|
||||
uni.navigateBack()
|
||||
|
||||
759
packageB/pages/userCenter/professionPath.vue
Normal file
759
packageB/pages/userCenter/professionPath.vue
Normal file
@@ -0,0 +1,759 @@
|
||||
<template>
|
||||
<view class="index-wrap">
|
||||
<view class="head-bar" :style="{'margin-top': barHeight + 5 + 'px'}">
|
||||
<view class="go-back" @click="goBack"></view>
|
||||
<text>职业路径</text>
|
||||
</view>
|
||||
<view class="content">
|
||||
<view class="section">
|
||||
<view class="title-h1">
|
||||
推荐职业
|
||||
</view>
|
||||
<view class="table" v-if="jobList.length > 0">
|
||||
<view class="tr">
|
||||
<view class="th">
|
||||
</view>
|
||||
<view class="th">
|
||||
职业名称
|
||||
</view>
|
||||
<view class="th">
|
||||
测评推荐
|
||||
</view>
|
||||
<view class="th">
|
||||
我的意向
|
||||
</view>
|
||||
<view class="th">
|
||||
专业匹配
|
||||
</view>
|
||||
</view>
|
||||
<view class="tr" v-for="(item,index) in jobList" @click="checkedJob(item)" :key="index">
|
||||
<view class="td">
|
||||
<view class="checked-btn" :class="checkedCode==item.EncodeId?'on':''"></view>
|
||||
</view>
|
||||
<view class="td">
|
||||
{{item.Name}}
|
||||
</view>
|
||||
<view class="td">
|
||||
<view class="is-has" v-if="item.IsRecommend"></view>
|
||||
</view>
|
||||
<view class="td">
|
||||
<view class="is-has" v-if="item.IsIntention"></view>
|
||||
</view>
|
||||
<view class="td">
|
||||
<view class="is-has" v-if="item.IsSpecialtyMatch"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="empty" v-else>
|
||||
<view class="icon"></view>
|
||||
<view class="txt">暂无推荐职业,请先进行兴趣测评</view>
|
||||
<view class="nav-btn" @click="navTest">
|
||||
去测评
|
||||
</view>
|
||||
</view>
|
||||
<view class="title-h1">
|
||||
职业介绍
|
||||
</view>
|
||||
<view class="desc">
|
||||
<view class="strong">
|
||||
{{jobName}}
|
||||
</view>
|
||||
{{jobIntroduce}}
|
||||
</view>
|
||||
<view class="title-h1">
|
||||
发展路径
|
||||
</view>
|
||||
<view class="desc">
|
||||
<view class="strong">
|
||||
{{jobPath}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="path-map">
|
||||
<view class="origin-item">
|
||||
大学生
|
||||
</view>
|
||||
<view class="path-list" :class="isLoadingEnd?'show':'ing'">
|
||||
<view class="line-1">
|
||||
<view class="drop" :class="'drop-' + (index+1)" v-for="(item,index) in academicRequire" :key="index" @click="showName(item)">
|
||||
<view class="txt">
|
||||
{{item}}
|
||||
</view>
|
||||
<view class="icon"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="line-2">
|
||||
<view class="drop" :class="'drop-' + (index+1)" v-for="(item,index) in generalSkillRequire" :key="index" @click="showName(item)">
|
||||
<view class="txt">
|
||||
{{item}}
|
||||
</view>
|
||||
<view class="icon"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="line-3">
|
||||
<view class="drop" :class="'drop-' + (index+1)" v-for="(item,index) in certificateRequire" :key="index" @click="showName(item)">
|
||||
<view class="txt">
|
||||
{{item}}
|
||||
</view>
|
||||
<view class="icon"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="line-4">
|
||||
<view class="drop" :class="'drop-' + (index+1)" v-for="(item,index) in jobSkill" :key="index" @click="showName(item)">
|
||||
<view class="txt">
|
||||
{{item}}
|
||||
</view>
|
||||
<view class="icon"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="line-5">
|
||||
<view class="drop" :class="'drop-' + (index+1)" v-for="(item,index) in professionalRequire" :key="index" @click="showName(item)">
|
||||
<view class="txt">
|
||||
{{item}}
|
||||
</view>
|
||||
<view class="icon"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="line-6">
|
||||
<view class="drop" :class="'drop-' + (index+1)" v-for="(item,index) in jobRequire" :key="index" @click="showName(item)">
|
||||
<view class="txt">
|
||||
{{item}}
|
||||
</view>
|
||||
<view class="icon"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="terminal-point">
|
||||
{{jobName}}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from "@/apiB/studentProfile.js"
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
barHeight: wx.getWindowInfo().statusBarHeight,
|
||||
jobList: [],//
|
||||
checkedCode: null,
|
||||
jobIntroduce: "",
|
||||
jobName: "",
|
||||
jobPath: "",
|
||||
academicRequire: [], //
|
||||
generalSkillRequire: [],
|
||||
certificateRequire: [],
|
||||
jobRequire: [],
|
||||
jobSkill: [],
|
||||
professionalRequire: [],
|
||||
isLoadingEnd: false,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
// 获取推荐职业
|
||||
async init(){
|
||||
uni.showLoading({
|
||||
title: "加载中..."
|
||||
})
|
||||
await this.queryCareerPath();
|
||||
await this.queryPathInfo();
|
||||
uni.hideLoading();
|
||||
},
|
||||
async queryCareerPath() {
|
||||
const res = await api.queryCareerPath();
|
||||
if (res.Result == 1) {
|
||||
const list = res.Data.JobList
|
||||
this.jobList = list;
|
||||
if(list.length>0){
|
||||
this.checkedCode = list[0].EncodeId;
|
||||
}
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: res.Message,
|
||||
icon: "none"
|
||||
})
|
||||
}
|
||||
},
|
||||
// 获取职业
|
||||
async queryPathInfo(){
|
||||
this.isLoadingEnd = false;
|
||||
const res = await api.queryPathInfo(this.checkedCode);
|
||||
if (res.Result == 1) {
|
||||
let data = res.Data;
|
||||
this.jobName = data.Name;
|
||||
this.jobIntroduce = data.JobIntroduce;
|
||||
this.jobPath = data.JobPath;
|
||||
this.academicRequire = JSON.parse(data.AcademicRequire).slice(0,5);
|
||||
this.generalSkillRequire = JSON.parse(data.GeneralSkillRequire).slice(0,5);
|
||||
this.certificateRequire = JSON.parse(data.CertificateRequire).slice(0,5);
|
||||
|
||||
this.jobRequire = JSON.parse(data.JobRequire).slice(0,5);
|
||||
this.jobSkill = JSON.parse(data.JobSkill).slice(0,5);
|
||||
this.professionalRequire = JSON.parse(data.ProfessionalRequire).slice(0,5);
|
||||
this.isLoadingEnd = true;
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: res.Message,
|
||||
icon: "none"
|
||||
})
|
||||
}
|
||||
},
|
||||
// 去测评
|
||||
navTest(){
|
||||
uni.navigateTo({
|
||||
url: `/pagesTest/interestTestCollect/interestTestTitle`
|
||||
})
|
||||
},
|
||||
//选中职业
|
||||
checkedJob(ITEM){
|
||||
this.checkedCode = ITEM.EncodeId;
|
||||
this.queryPathInfo();
|
||||
},
|
||||
showName(ITEM){
|
||||
uni.showToast({
|
||||
title: ITEM,
|
||||
icon:"none"
|
||||
});
|
||||
},
|
||||
goBack(){
|
||||
uni.navigateBack(-1);
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
$image-oss-url: "https://51xuanxiao.oss-cn-hangzhou.aliyuncs.com/Resource/xcx_sygh";
|
||||
page {
|
||||
background: #EEF1F8 url("#{$image-oss-url}/17.png") no-repeat;
|
||||
background-size: contain;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
$image-oss-url: "https://51xuanxiao.oss-cn-hangzhou.aliyuncs.com/Resource/xcx_sygh";
|
||||
.head-bar {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
height: 60rpx;
|
||||
line-height: 60rpx;
|
||||
.go-back {
|
||||
position: absolute;
|
||||
left: 10rpx;
|
||||
top: 0;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAABUklEQVRoQ+3ZOwrCQBCA4UlyCVsrQauQdPYewcNYWXoHK1s9gYeYdIKlracYWYggkgiTnccuaB3D/+1sYEMKyPxXZN4Pf0DMBOu6XpZluQOANRGduq7bc+/nNoE+/gwAq3c0IrJ72H/grtDQ9UPxAPBAxDn3/uaAkfjQvUXES9IA6fiANZuARrwZQCveBKAZrw7QjlcFWMSrAaziVQCW8eIA63hRgEe8GMArXgTgGR8N8I6PAqQQPxmQSvxkQNM0VwDYfJ3dJ53nuef/7+vZx+l+9W8pxE+aQNu2CyK6ZwsI4VlvoQDI/iFOCcF+iD/3fgqTiAKkMIlogDdCBOCJEAN4IUQBHghxgDVCBWCJUANYIVQBFgh1gDbCBKCJMANoIUwBGghzwA/EExFn3HdkF8AYIpvPrO9VDu8TVVUdiKghomNWH7q5W2Xserct9Af0K/AChQ/cMY9OGScAAAAASUVORK5CYII=") center no-repeat;
|
||||
background-size: 38rpx 38rpx;
|
||||
}
|
||||
}
|
||||
.empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-top: 60rpx;
|
||||
padding-bottom: 100rpx;
|
||||
.icon {
|
||||
width: 240rpx;
|
||||
height: 240rpx;
|
||||
background: url("#{$image-oss-url}/empty.png") no-repeat;
|
||||
background-size: 100%;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.txt {
|
||||
font-size: 28rpx;
|
||||
color: #A4B3E5;
|
||||
}
|
||||
.nav-btn {
|
||||
width: 335rpx;
|
||||
height: 80rpx;
|
||||
margin: 40rpx auto 0;
|
||||
background-color: #1989fa;
|
||||
border-radius: 40rpx;
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
line-height: 80rpx;
|
||||
}
|
||||
}
|
||||
.index-wrap {
|
||||
.content {
|
||||
padding: 0 20rpx;
|
||||
margin-top: 60rpx;
|
||||
.title-h1 {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
margin-bottom: 30rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
.section {
|
||||
width: 650rpx;
|
||||
padding: 30rpx 30rpx 50rpx;
|
||||
background-color: #ffffff;
|
||||
border-radius: 14rpx;
|
||||
margin-bottom: 50rpx;
|
||||
.table {
|
||||
border-left: 2rpx solid #eef2fd;
|
||||
border-top: 2rpx solid #eef2fd;
|
||||
border-right: 2rpx solid #eef2fd;
|
||||
border-radius: 6rpx;
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
margin-bottom: 30rpx;
|
||||
max-height: 400rpx;
|
||||
.tr {
|
||||
display: -webkit-box;
|
||||
.th {
|
||||
width: 145rpx;
|
||||
height: 72rpx;
|
||||
line-height: 72rpx;
|
||||
text-align: center;
|
||||
background: #F6F9FE;
|
||||
font-size: 24rpx;
|
||||
color: #333333;
|
||||
border-bottom: 2rpx solid #eef2fd;
|
||||
&:nth-child(1){
|
||||
width: 60rpx;
|
||||
}
|
||||
&:nth-child(2){
|
||||
width: 250rpx;
|
||||
}
|
||||
}
|
||||
.td {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 145rpx;
|
||||
min-height: 72rpx;
|
||||
font-size: 24rpx;
|
||||
color: #333333;
|
||||
text-align: center;
|
||||
border-bottom: 2rpx solid #eef2fd;
|
||||
&:nth-child(1){
|
||||
width: 60rpx;
|
||||
}
|
||||
&:nth-child(2){
|
||||
width: 250rpx;
|
||||
word-break: break-all;
|
||||
}
|
||||
.checked-btn {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 50%;
|
||||
border: 2rpx solid #999999;
|
||||
&.on {
|
||||
position: relative;
|
||||
border-color: #1989fa;
|
||||
background: #1989fa;
|
||||
&:before {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%,-50%);
|
||||
content: "";
|
||||
display: block;
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
.is-has {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAAPXSURBVGiB7VlBbttIEKweLrXH5QKW4JuZH/gHa79g5RfEeoFpwBJ8o3Tz2gGkvCD6gZMXWE/QD8TcDDvA8hopntpDxlnZniFHJGMggOomcWbY1Wx2VzeBLbbY4peGNH3gzhkPRBCLYI9ABCISQUbiswjy5RKzfCJ5U/erTWD3nPHDAw5E8DeArs8eEjMSn759w7QumVoEds6YKIUTAHGV/SRyEUyWS7yvSqQSgXafxyJIqxr+HCRyEqMv72Sy6d6NCXQGTAEMN93nAxLT1QqnmzwNbwJRwihsIRUgqWyhH+ZK4ej2QjKfxcr31DDE+BWMB4B9rXETJYx8FnsR6AyYiuC4tmn+iFu/Y+yzsDSEOgN2AVw3YpbbiikAgC+cNLy7lFHRVp8n4OWJipgrhTd3/0gPhC1khrvnLMx0vxVdNBmnkVT5HAQm95dy+uM3cSCWeNBECqDnOscZQlHCKAyxELF6pha0xul6zo8Sxq0WFq71SuGNKys5QygM0X0N433woHHiulb0Dvy1kWV+GDqMLyxcUqCxrARMDvYSZr4gMHFlFFN5iwpX3O5z33bBSiAMETccPvPVEoXpEIJZ0WUSB7b/XSFkZVsFJHKlcFSmb5QUE1QKf1j/d9zUx/sfSUzJYs+JYOKja24vJCPgfLlJezq31gFR2HMckpMYPW9ETModW+TGvKySwjRFmkgtlXgd/gQcxs+CAD2bNw2ZXrvPWOT/WNUapy8OshiudXWdZQ8hjc+Wv/e1xtsilRgEaxVTMP3yTpzh1Rkw1RqLEq+vw7+QKfVysclKwzDEYueMVll9eyEZBFMS+fKr3fu754w7Ay6waVMk9lphJaC1u7CIIFIK43afH2xPQwlGIpjYsk67z32tcVNFXzmiwq6FooRRq4V/Pc717p7afR4DGFetL1rj0BaSTjHXGfDasxpnSuGwiIQZAnzYxOB1kMjvr+RP2zWnFiLxyfP8WGt3z2AkQL2e4rHhscBJIAgwK9En6+ia3uEJTDNyU1eWULud6SRgQsLJ3IKhiXPAvEda47q28cSsKB0XtpSmivo+BYggfWwBwxbSJjTVk9piQWlPTJaoyKeItca43edxEyMYolxHeQ22fuY0zgUSs/srOSxb5zUXuruUUZnqbBjZaoUjn4Xek7kgQO+VSGRK4dB3PrrxcLc94E8bMRYpXhcqjdd3zpiIIG247SydwtlQ+QOH0UsnJJJaRARTJRht4vWn22ti95yx1nhrJmvWxtuCjMDHQOF9VcMf0ehHPtNadkWwR36fbJj+Onv80Adgfn8l8ybvu8UWW/zC+A/xK5HWhynIGQAAAABJRU5ErkJggg==") center no-repeat;
|
||||
background-size: 90%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.desc {
|
||||
font-size: 28rpx;
|
||||
color: #666666;
|
||||
margin-bottom: 50rpx;
|
||||
.strong {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
.path-map {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
.terminal-point,
|
||||
.origin-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 144rpx;
|
||||
height: 144rpx;
|
||||
color: #ffffff;
|
||||
font-size: 28rpx;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 20rpx;
|
||||
background-color: #1989fa;
|
||||
animation: change 2s linear infinite 0s;
|
||||
}
|
||||
@keyframes change {
|
||||
0% {
|
||||
box-shadow: 0 0 1px 6rpx #e8f3fe;
|
||||
}
|
||||
25% {
|
||||
box-shadow: 0 0 1px 12rpx #e8f3fe;
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 1px 16rpx #e8f3fe;
|
||||
}
|
||||
75% {
|
||||
box-shadow: 0 0 1px 12rpx #e8f3fe;
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 1px 6rpx #e8f3fe;;
|
||||
}
|
||||
}
|
||||
.path-list {
|
||||
position: relative;
|
||||
padding: 0 30rpx;
|
||||
height: 1160rpx;
|
||||
width: 690rpx;
|
||||
margin-bottom: 30rpx;
|
||||
.drop {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
.txt {
|
||||
width: 150rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
height: 52rpx;
|
||||
padding: 0 10rpx;
|
||||
line-height: 52rpx;
|
||||
margin-bottom: 10rpx;
|
||||
background-color: #ffffff;
|
||||
border-radius: 26rpx;
|
||||
color: #1989fa;
|
||||
font-size: 28rpx;
|
||||
border: solid 2rpx #1989fa;
|
||||
}
|
||||
.icon {
|
||||
width: 32rpx;
|
||||
height: 44rpx;
|
||||
margin: 0 auto;
|
||||
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAsCAMAAAAzZkq9AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAByFBMVEUZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoZifoAAAAhPv4wAAAAlnRSTlMAMGSDm56PbUsPIoDY77RPGv7IUYz6uSGZ9w267UOvJU7FG2OfAgHwvVZzqPFxPBPW0IUM4Oid9doyq/Pi4/LRvm7OePgYl1x65NweDpWx2y9FkHQfBRX5zcbnXuZhR6zlsinqVesZV+42OHBmBolpMwQIqaMrmIKiHTpCaqotUHw9EaUj9MOBaCAkb4jUyaeO4fvdrUZV3EhjAAAAAWJLR0SX5m4brwAAAAd0SU1FB+kKHg8QKp3MRF0AAAH3SURBVDjLfZT5P5tBEMaHCEop6qyUV9QV4oqjrgqNO62rlCC0SJBWaV093UfrPjp/r/fNm92dJO/r+SUz830+u5uZdxeAKCLSEGWMjol9BFqKi3+MASUkPgnDSVHJSJXyNJinpmGo0jMIz0QNZcUx/CwbNWV6HjDkoJ5y/VzS5ZincHOyvgHzZcOLBzimFUBhEcmLU0skS2kZqVihnGSR6qErKkWpCqpFIrH/bavhtVoo5nGdaJy5nhXr4SU3NJDWNrJiEzSzsIXOxsCqr6CVhXZqaBOGPBa2U8NrcQYjCx0dxNDJBwZd/JDdxNDDir3Qxw0tTs7f8OJbgH7hGAhwvi3iIMAQ6fXwO4ARyS4KTbJ99D0d39i4i6YTyoJW/Wn3qltO6hqmVINbj0+HtSVYM6IvHzQNH0nnZjX4HB1OxnwYjwm+nB09IdwSer09lUHcq/FAlAq8sBgKbUu+T2Bh3A6fl7+srHL61fvNgcpFdKsf8xqsKz+mjUGVb9bKWfOW1fodPPLHOv4D4Oev7t8mudqqHKVO9lb/Yatt77BodVe5tV2wh7gMOnLmoAsqlAuqp30s8r8vVflaL+OB4RDRB1AwrAziqPP4xL3y17PnHDX/W5JOz4zncvVCnYfNd6Q5zcttspz3Krv9ev7GDxyu25S74//qtveUBePSekmnEAAAAABJRU5ErkJggg==") no-repeat;
|
||||
background-size: 100%;
|
||||
}
|
||||
}
|
||||
&.ing {
|
||||
.drop {
|
||||
transform: scale(0.1);
|
||||
}
|
||||
}
|
||||
&.show {
|
||||
.line-1{
|
||||
.drop-1 {
|
||||
animation: showDot 0.2s linear 0s;
|
||||
}
|
||||
.drop-2 {
|
||||
animation: showDot 0.2s linear 0.2s;
|
||||
}
|
||||
.drop-3 {
|
||||
animation: showDot 0.25s linear 1.1s;
|
||||
}
|
||||
.drop-4 {
|
||||
animation: showDot 0.2s linear 1.8s;
|
||||
}
|
||||
.drop-5 {
|
||||
animation: showDot 0.2s linear 2s;
|
||||
}
|
||||
}
|
||||
.line-2{
|
||||
.drop-1 {
|
||||
animation: showDot 0.2s linear 0.3s;
|
||||
}
|
||||
.drop-2 {
|
||||
animation: showDot 0.2s linear 0.5s;
|
||||
}
|
||||
.drop-3 {
|
||||
animation: showDot 0.2s linear 1.4s;
|
||||
}
|
||||
.drop-4 {
|
||||
animation: showDot 0.2s linear 1.9s;
|
||||
}
|
||||
.drop-5 {
|
||||
animation: showDot 0.2s linear 2.1s;
|
||||
}
|
||||
}
|
||||
.line-3{
|
||||
.drop-1 {
|
||||
animation: showDot 0.2s linear 0.1s;
|
||||
}
|
||||
.drop-2 {
|
||||
animation: showDot 0.2s linear 0.8s;
|
||||
}
|
||||
.drop-3 {
|
||||
animation: showDot 0.25s linear 1.3s;
|
||||
}
|
||||
.drop-4 {
|
||||
animation: showDot 0.2s linear 1.5s;
|
||||
}
|
||||
.drop-5 {
|
||||
animation: showDot 0.2s linear 1.7s;
|
||||
}
|
||||
}
|
||||
.line-4{
|
||||
.drop-1 {
|
||||
animation: showDot 0.2s linear 0.6s;
|
||||
}
|
||||
.drop-2 {
|
||||
animation: showDot 0.2s linear 0.9s;
|
||||
}
|
||||
.drop-3 {
|
||||
animation: showDot 0.25s linear 1.4s;
|
||||
}
|
||||
.drop-4 {
|
||||
animation: showDot 0.2s linear 1.6s;
|
||||
}
|
||||
.drop-5 {
|
||||
animation: showDot 0.2s linear 1.8s;
|
||||
}
|
||||
}
|
||||
.line-5{
|
||||
.drop-1 {
|
||||
animation: showDot 0.2s linear 0.2s;
|
||||
}
|
||||
.drop-2 {
|
||||
animation: showDot 0.2s linear 0.9s;
|
||||
}
|
||||
.drop-3 {
|
||||
animation: showDot 0.2s linear 1.4s;
|
||||
}
|
||||
.drop-4 {
|
||||
animation: showDot 0.2s linear 1.7s;
|
||||
}
|
||||
.drop-5 {
|
||||
animation: showDot 0.2s linear 1.9s;
|
||||
}
|
||||
}
|
||||
.line-6{
|
||||
.drop-1 {
|
||||
animation: showDot 0.2s linear 0.4s;
|
||||
}
|
||||
.drop-2 {
|
||||
animation: showDot 0.2s linear 0.8s;
|
||||
}
|
||||
.drop-3 {
|
||||
animation: showDot 0.25s linear 1s;
|
||||
}
|
||||
.drop-4 {
|
||||
animation: showDot 0.2s linear 1.9s;
|
||||
}
|
||||
.drop-5 {
|
||||
animation: showDot 0.2s linear 2.2s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.line-1 {
|
||||
position: absolute;
|
||||
left: 65rpx;
|
||||
top: 20rpx;
|
||||
width: 190rpx;
|
||||
height: 1168rpx;
|
||||
background: url("#{$image-oss-url}/gaoxiao/jobCareer/path-1.png") center no-repeat;
|
||||
background-size: 100%;
|
||||
.drop-1 {
|
||||
left: 40rpx;
|
||||
top: -13rpx;
|
||||
}
|
||||
.drop-2 {
|
||||
left: -37rpx;
|
||||
top: 202rpx;
|
||||
}
|
||||
.drop-3 {
|
||||
left: -77rpx;
|
||||
top: 381rpx;
|
||||
}
|
||||
.drop-4 {
|
||||
left: -48rpx;
|
||||
top: 750rpx;
|
||||
}
|
||||
.drop-5 {
|
||||
left: 10rpx;
|
||||
top: 910rpx;
|
||||
}
|
||||
}
|
||||
.line-2 {
|
||||
position: absolute;
|
||||
left: 180rpx;
|
||||
top: 20rpx;
|
||||
width: 95rpx;
|
||||
height: 1169rpx;
|
||||
background: url("#{$image-oss-url}/gaoxiao/jobCareer/path-2.png") center no-repeat;
|
||||
background-size: 100%;
|
||||
.drop-1 {
|
||||
left: -58rpx;
|
||||
top: 146rpx;
|
||||
}
|
||||
.drop-2 {
|
||||
left: -76rpx;
|
||||
top: 316rpx;
|
||||
}
|
||||
.drop-3 {
|
||||
left: -82rpx;
|
||||
top: 480rpx;
|
||||
}
|
||||
.drop-4 {
|
||||
left: -85rpx;
|
||||
top: 633rpx;
|
||||
}
|
||||
.drop-5 {
|
||||
left: -57rpx;
|
||||
top: 822rpx;
|
||||
}
|
||||
}
|
||||
.line-3 {
|
||||
position: absolute;
|
||||
left: 310rpx;
|
||||
top: 0;
|
||||
width: 16rpx;
|
||||
height: 1169rpx;
|
||||
background: url("#{$image-oss-url}/gaoxiao/jobCareer/path-3.png") center no-repeat;
|
||||
background-size: 100%;
|
||||
.drop-1 {
|
||||
left: -78rpx;
|
||||
top: 90rpx;
|
||||
}
|
||||
.drop-2 {
|
||||
left: -78rpx;
|
||||
top: 264rpx;
|
||||
}
|
||||
.drop-3 {
|
||||
left: -78rpx;
|
||||
top: 433rpx;
|
||||
}
|
||||
.drop-4 {
|
||||
left: -78rpx;
|
||||
top: 589rpx;
|
||||
}
|
||||
.drop-5 {
|
||||
left: -78rpx;
|
||||
top: 773rpx;
|
||||
}
|
||||
}
|
||||
.line-4 {
|
||||
position: absolute;
|
||||
left: 410rpx;
|
||||
top: 0;
|
||||
width: 16rpx;
|
||||
height: 1169rpx;
|
||||
background: url("#{$image-oss-url}/gaoxiao/jobCareer/path-4.png") center no-repeat;
|
||||
background-size: 100%;
|
||||
.drop-1 {
|
||||
left: -78rpx;
|
||||
top: 153rpx;
|
||||
}
|
||||
.drop-2 {
|
||||
left: -78rpx;
|
||||
top: 336rpx;
|
||||
}
|
||||
.drop-3 {
|
||||
left: -78rpx;
|
||||
top: 504rpx;
|
||||
}
|
||||
.drop-4 {
|
||||
left: -78rpx;
|
||||
top: 656rpx;
|
||||
}
|
||||
.drop-5 {
|
||||
left: -78rpx;
|
||||
top: 839rpx;
|
||||
}
|
||||
}
|
||||
.line-5 {
|
||||
position: absolute;
|
||||
left: 465rpx;
|
||||
top: 16rpx;
|
||||
width: 60rpx;
|
||||
height: 1168rpx;
|
||||
background: url("#{$image-oss-url}/gaoxiao/jobCareer/path-5.png") center no-repeat;
|
||||
background-size: 100%;
|
||||
.drop-1 {
|
||||
left: -72rpx;
|
||||
top: -41rpx;
|
||||
}
|
||||
.drop-2 {
|
||||
left: -39rpx;
|
||||
top: 219rpx;
|
||||
}
|
||||
.drop-3 {
|
||||
left: -31rpx;
|
||||
top: 446rpx;
|
||||
}
|
||||
.drop-4 {
|
||||
left: -36rpx;
|
||||
top: 695rpx;
|
||||
}
|
||||
.drop-5 {
|
||||
left: -61rpx;
|
||||
top: 933rpx;
|
||||
}
|
||||
}
|
||||
.line-6 {
|
||||
position: absolute;
|
||||
left: 480rpx;
|
||||
top: 20rpx;
|
||||
width: 186rpx;
|
||||
height: 1168rpx;
|
||||
background: url("#{$image-oss-url}/gaoxiao/jobCareer/path-6.png") center no-repeat;
|
||||
background-size: 100%;
|
||||
.drop-1 {
|
||||
left: -5rpx;
|
||||
top: 57rpx;
|
||||
}
|
||||
.drop-2 {
|
||||
left: 66rpx;
|
||||
top: 273rpx;
|
||||
}
|
||||
.drop-3 {
|
||||
left: 90rpx;
|
||||
top: 518rpx;
|
||||
}
|
||||
.drop-4 {
|
||||
left: 54rpx;
|
||||
top: 753rpx;
|
||||
}
|
||||
.drop-5 {
|
||||
left: -58rpx;
|
||||
top: 1028rpx;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes showDot {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.1);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,138 +0,0 @@
|
||||
<template>
|
||||
<!-- <AppLayout title=""> -->
|
||||
<view class="tab-container">
|
||||
<image src="../../static/images/train/bj.jpg" mode=""></image>
|
||||
<view>
|
||||
<view class="btns" @click="jumps('/packageB/train/video/videoList')">
|
||||
<image src="/static/images/train/spxx-k.png" mode=""></image>
|
||||
<view>
|
||||
<text>培训视频</text>
|
||||
<view class="btn">
|
||||
<text>立即查看</text>
|
||||
<image src="/static/images/train/arrow.png" mode=""></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="btns" @click="jumps('/packageB/train/practice/startPracticing')">
|
||||
<image src="/static/images/train/zxxl-k.png" mode=""></image>
|
||||
<view>
|
||||
<text>专项练习</text>
|
||||
<view class="btn">
|
||||
<text>立即查看</text>
|
||||
<image src="/static/images/train/arrow.png" mode=""></image>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<view class="btns" @click="jumps('')">
|
||||
<image src="/static/images/train/mnks-k.png" mode=""></image>
|
||||
<view>
|
||||
<text>模拟考试</text>
|
||||
<view class="btn">
|
||||
<text>立即查看</text>
|
||||
<image src="/static/images/train/arrow.png" mode=""></image>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<view class="btns" @click="jumps('')">
|
||||
<image src="/static/images/train/ctb-k.png" mode=""></image>
|
||||
<view>
|
||||
<text>错题本 </text>
|
||||
<view class="btn" style="margin-left: 13%;">
|
||||
<text>立即查看</text>
|
||||
<image src="/static/images/train/arrow.png" mode=""></image>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- </AppLayout> -->
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, inject, watch, ref, onMounted } from 'vue';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
const { $api, navTo, vacanciesTo, formatTotal, config } = inject('globalFunction');
|
||||
import useUserStore from '@/stores/useUserStore';
|
||||
import useDictStore from '@/stores/useDictStore';
|
||||
|
||||
function jumps(url){
|
||||
navTo(url);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.tab-container{
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
// background: url('@/static/images/train/bj.jpg') center center no-repeat;
|
||||
// background-size: 100% 100%;
|
||||
image{
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
view{
|
||||
padding: 100rpx 28rpx 0;
|
||||
.btns{
|
||||
width: 100%
|
||||
height: 170rpx;
|
||||
border-radius: 5rpx
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box
|
||||
color: #000
|
||||
text-align: center
|
||||
margin-bottom: 50rpx
|
||||
position: relative
|
||||
image{
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
view{
|
||||
padding: 0 0 0 10%;
|
||||
box-sizing: border-box;
|
||||
position: absolute
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
align-items: center
|
||||
font-weight: bold;
|
||||
font-size: 36rpx;
|
||||
color: #595959;
|
||||
.btn{
|
||||
margin-left: 8%;
|
||||
background-color: #3A92FF;
|
||||
position: static;
|
||||
color: #fff;
|
||||
font-size: 22rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center
|
||||
width: 140rpx;
|
||||
height: 50rpx;
|
||||
border-radius: 5rpx
|
||||
font-weight: 500;
|
||||
padding: 0;
|
||||
image{
|
||||
position: static;
|
||||
width: 14rpx;
|
||||
height: 22rpx;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<template>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -1,361 +0,0 @@
|
||||
<template>
|
||||
<div class="app-box">
|
||||
<image src="../../../static/images/train/bj.jpg" class="bjImg" mode=""></image>
|
||||
<div class="con-box">
|
||||
<div class="header">
|
||||
<div>正确率:0%</div>
|
||||
<div>用时:00:00</div>
|
||||
<div class="headBtn">暂停</div>
|
||||
</div>
|
||||
<div class="problemCard">
|
||||
<div v-for="(item,index) in problemData" :key="index">
|
||||
<template v-if="questionIndex==(index+1)">
|
||||
<div class="problemTitle">
|
||||
<span class="titleType" v-if="item.type=='single'">单选题</span>
|
||||
<span class="titleType" v-if="item.type=='multiple'">多选题</span>
|
||||
<span class="titleType" v-if="item.type=='judge'">判断题</span>
|
||||
<span>{{item.content}}</span>
|
||||
</div>
|
||||
<div class="options" v-if="item.type=='single'">
|
||||
<div class="opt" @click="selected(i)" :class="radio!==''&&i==radio?'active':''" v-for="(val,i) in parseOptions(item.trainChooses)">
|
||||
<div class="optLab">{{indexToLetter(i)}}</div>
|
||||
<span>{{val}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="options" v-if="item.type=='multiple'">
|
||||
<div class="opt" @click="selected2(i)" :class="judgment(i)?'active':''" v-for="(val,i) in parseOptions(item.trainChooses)">
|
||||
<div class="optLab">{{indexToLetter(i)}}</div>
|
||||
<span>{{val}}</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="options" v-if="item.type=='judge'">
|
||||
<div class="opt" @click="selected3('正确')" :class="radio2=='正确'?'active':''">
|
||||
<span>正确</span>
|
||||
</div>
|
||||
<div class="opt" @click="selected3('错误')" :class="radio2=='错误'?'active':''">
|
||||
<span>错误</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="analysis">
|
||||
<div class="analysisHead correct">
|
||||
<div>回答正确!</div>
|
||||
<div></div>
|
||||
</div>
|
||||
<!-- <div class="analysisHead errors" v-if="judgWhether=='错误'">
|
||||
<div>回答错误!</div>
|
||||
<div></div>
|
||||
</div> -->
|
||||
<div class="analysisCon">
|
||||
<div class="parse1">正确答案:</div>
|
||||
<div class="parse2" v-if="item.type=='single'" style="color: #30A0FF;font-weight: bold;">{{String.fromCharCode(65 + Number(item.answer))}}.</div>
|
||||
<div class="parse2" v-if="item.type=='multiple'" style="color: #30A0FF;font-weight: bold;">
|
||||
<span v-for="(val,i) in parseOptions(item.answer)">{{indexToLetter(val-1)}}.</span>
|
||||
</div>
|
||||
<div class="parse2" v-if="item.type=='judge'" style="color: #30A0FF;font-weight: bold;">{{item.answer}}</div>
|
||||
</div>
|
||||
<div class="analysisCon">
|
||||
<div class="parse1">答案解析:</div>
|
||||
<div class="parse2">{{item.answerDesc}}</div>
|
||||
</div>
|
||||
<div class="analysisCon">
|
||||
<div class="parse1">知识点:</div>
|
||||
<div>
|
||||
<el-tag style="margin-right: 10px;">{{item.knowledgePoint}}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="problemBtns">
|
||||
<div class="events">提交答案</div>
|
||||
<div>下一题</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="footerLeft">
|
||||
<div class="zuo events"></div>
|
||||
<div class="you"></div>
|
||||
<div style="text-align: center;font-size: 24rpx;">
|
||||
<div>⭐</div>
|
||||
<div>收藏</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div></div>
|
||||
<div class="footer">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, inject, watch, ref, onMounted } from 'vue';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
const { $api, navTo, vacanciesTo, formatTotal, config } = inject('globalFunction');
|
||||
import useUserStore from '@/stores/useUserStore';
|
||||
import useDictStore from '@/stores/useDictStore';
|
||||
|
||||
const radio = ref('');
|
||||
const radio2 = ref('');
|
||||
const checkList = ref([]);
|
||||
const questionIndex = ref(1);
|
||||
const correctIndex = ref(0);
|
||||
const errorsIndex = ref(0);
|
||||
const accuracyRate = ref(0);
|
||||
const problemData = reactive([
|
||||
{
|
||||
type:'single',
|
||||
content:"君不见黄河之水天上来,下一句是?",
|
||||
fraction:5,
|
||||
trainChooses:"奔流到海不复回,朝如青丝暮成雪,人生得意须尽欢,莫使金樽空对月",
|
||||
},{
|
||||
type:'multiple',
|
||||
content:"以下哪些是欧姆定律的适用条件?",
|
||||
fraction:8,
|
||||
trainChooses:"线性电阻,恒定温度,金属导体,半导体材料",
|
||||
|
||||
},{
|
||||
type:'judge',
|
||||
content:"功率越大的电器,其电阻值越小",
|
||||
fraction:2,
|
||||
trainChooses:[
|
||||
"正确",
|
||||
"错误",
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
function selected(i){
|
||||
radio.value=i
|
||||
};
|
||||
//多选
|
||||
function selected2(i){
|
||||
let arr=checkList.value.join(",")
|
||||
if(arr.indexOf(i) !== -1){
|
||||
const index = checkList.value.indexOf(i);
|
||||
if (index !== -1) {
|
||||
checkList.value.splice(index, 1);
|
||||
}
|
||||
}else{
|
||||
checkList.value.push(i)
|
||||
}
|
||||
};
|
||||
function judgment(i){
|
||||
let arr=checkList.value.join(",")
|
||||
if(arr.indexOf(i) !== -1){
|
||||
return true
|
||||
}else{
|
||||
return false
|
||||
}
|
||||
};
|
||||
function selected3(i){
|
||||
radio2.value=i
|
||||
};
|
||||
// 解析选项
|
||||
function parseOptions(options) {
|
||||
if (!options) return [];
|
||||
// 假设options是字符串格式,以分号分隔
|
||||
if (typeof options === 'string') {
|
||||
return options.split(',').filter(opt => opt.trim());
|
||||
}
|
||||
// 如果是数组,直接返回
|
||||
if (Array.isArray(options)) {
|
||||
return options;
|
||||
}
|
||||
return [];
|
||||
};
|
||||
function indexToLetter(index) {
|
||||
// 将索引转换为对应的字母
|
||||
return String.fromCharCode(65 + index);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.app-box{
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
.bjImg{
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.con-box{
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top:0;
|
||||
z-index: 10;
|
||||
padding: 20rpx 28rpx;
|
||||
box-sizing: border-box;
|
||||
.header{
|
||||
height: 100rpx;
|
||||
padding: 0 10rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: linear-gradient(0deg, #4285EC 0%, #0BBAFB 100%);
|
||||
color: #fff;
|
||||
font-size: 26rpx;
|
||||
border-radius: 10rpx
|
||||
.headBtn{
|
||||
background: #499FFF;
|
||||
border-radius: 4px;
|
||||
width: 100rpx;
|
||||
text-align: center;
|
||||
height: 50rpx;
|
||||
line-height: 50rpx;
|
||||
}
|
||||
}
|
||||
.problemCard{
|
||||
margin-top: 30rpx;
|
||||
.problemTitle{
|
||||
font-size: 30rpx;
|
||||
.titleType{
|
||||
display: inline-block;
|
||||
background-color: #499FFF;
|
||||
border-radius: 10rpx 10rpx 10rpx 0;
|
||||
padding: 8rpx 12rpx;
|
||||
color: #fff;
|
||||
font-size: 26rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
.options{
|
||||
margin-top: 30rpx;
|
||||
.opt{
|
||||
height: 60rpx;
|
||||
/* background-color: #F8F9FA; */
|
||||
border-radius: 5px;
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 30rpx;
|
||||
box-sizing: border-box;
|
||||
color: #808080;
|
||||
font-size: 30rpx;
|
||||
.optLab{
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
text-align: center;
|
||||
line-height: 40rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #8C8C8C;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
font-size: 32rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
.active{
|
||||
background: linear-gradient(90deg, #25A9F5 0%, #B1DBFF 100%);
|
||||
color: #fff!important;
|
||||
font-weight: bold;
|
||||
}
|
||||
.active>view{
|
||||
background-color: #fff!important;
|
||||
color: #25A9F5!important;
|
||||
}
|
||||
}
|
||||
.analysis{
|
||||
margin-top: 30rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 15rpx;
|
||||
border: 1px solid #10A8FF;
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
.analysisHead{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 32rpx;
|
||||
font-family: Microsoft YaHei;
|
||||
font-weight: bold;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
.correct{
|
||||
color: #67C23A;
|
||||
}
|
||||
.errors{
|
||||
color: #F06A6A;
|
||||
}
|
||||
.analysisCon{
|
||||
margin-top: 30rpx;
|
||||
.parse1{
|
||||
font-size: 30rpx;
|
||||
font-family: Microsoft YaHei;
|
||||
font-weight: bold;
|
||||
}
|
||||
.parse2{
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.problemBtns{
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: center
|
||||
view{
|
||||
width: 140rpx
|
||||
height: 50rpx
|
||||
text-align: center
|
||||
line-height: 50rpx
|
||||
background-color: #10A8FF
|
||||
color: #fff
|
||||
font-size: 28rpx
|
||||
border-radius: 5rpx
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
.footer{
|
||||
width: 100%;
|
||||
height: 120rpx;
|
||||
border-top: 1px solid #ddd
|
||||
position: fixed
|
||||
bottom: 0
|
||||
left: 0
|
||||
display: flex
|
||||
align-items: center
|
||||
justify-content: space-between
|
||||
.footerLeft{
|
||||
display: flex
|
||||
align-items: center
|
||||
font-size: 30rpx;
|
||||
.zuo{
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
border-top: 2px solid #666
|
||||
border-left: 2px solid #666
|
||||
transform: rotate(-45deg); /* 鼠标悬停时旋转360度 */
|
||||
margin-left: 50rpx;
|
||||
}
|
||||
.you{
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
border-right: 2px solid #666
|
||||
border-bottom: 2px solid #666
|
||||
transform: rotate(-45deg); /* 鼠标悬停时旋转360度 */
|
||||
margin-left: 30rpx;
|
||||
margin-right: 50rpx;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
.events{
|
||||
pointer-events: none; /* 这会禁用所有指针事件 */
|
||||
opacity: 0.5; /* 可选:改变透明度以视觉上表示不可点击 */
|
||||
cursor: not-allowed; /* 可选:改变鼠标光标样式 */
|
||||
}
|
||||
</style>
|
||||
@@ -1,316 +0,0 @@
|
||||
<template>
|
||||
<AppLayout :title="title" :show-bg-image="false" @onScrollBottom="getDataList('add')">
|
||||
<!-- <template #headerleft>
|
||||
<view class="btnback">
|
||||
<image src="@/static/icon/back.png" @click="navBack"></image>
|
||||
</view>
|
||||
</template> -->
|
||||
<template #headContent>
|
||||
<view class="collection-search">
|
||||
<view class="search-content">
|
||||
<view class="header-input button-click">
|
||||
<uni-icons class="iconsearch" color="#6A6A6A" type="search" size="22"></uni-icons>
|
||||
<input
|
||||
class="input"
|
||||
v-model="searchKeyword"
|
||||
@confirm="searchVideo"
|
||||
placeholder="输入视频名称"
|
||||
placeholder-class="inputplace"
|
||||
/>
|
||||
<uni-icons
|
||||
v-if="searchKeyword"
|
||||
class="clear-icon"
|
||||
type="clear"
|
||||
size="24"
|
||||
color="#999"
|
||||
@click="clearSearch"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<view class="main-list">
|
||||
<view class="list-title">
|
||||
<text>视频列表</text>
|
||||
<view class="title-line"></view>
|
||||
</view>
|
||||
<view class="video-grid" v-if="pageState.list.length">
|
||||
<view
|
||||
v-for="video in pageState.list"
|
||||
:key="video.id || video.videoId"
|
||||
class="video-item"
|
||||
:style="getItemBackgroundStyle('video-bg.png')"
|
||||
@click="playVideo(video)"
|
||||
>
|
||||
<view class="video-cover">
|
||||
<image
|
||||
:src="video.coverImage || video.videoCover || '/static/icon/video.png'"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
</view>
|
||||
<view class="video-info">
|
||||
{{ video.title || video.videoName || '未命名视频' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<empty v-else pdTop="200"></empty>
|
||||
</view>
|
||||
</AppLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject, ref, reactive } from 'vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
const { $api, navTo, navBack } = inject('globalFunction');
|
||||
|
||||
// state
|
||||
const title = ref('');
|
||||
const searchKeyword = ref('');
|
||||
const pageState = reactive({
|
||||
page: 0,
|
||||
list: [],
|
||||
total: 0,
|
||||
maxPage: 1,
|
||||
pageSize: 12,
|
||||
search: {},
|
||||
});
|
||||
const baseUrl = 'http://10.110.145.145/images/train/';
|
||||
const getItemBackgroundStyle = (imageName) => ({
|
||||
backgroundImage: `url(${baseUrl + imageName})`,
|
||||
backgroundSize: 'cover', // 覆盖整个容器
|
||||
backgroundPosition: 'center', // 居中
|
||||
backgroundRepeat: 'no-repeat'
|
||||
});
|
||||
// 模拟视频数据
|
||||
const mockVideoData = [
|
||||
{
|
||||
id: '1',
|
||||
title: '职业技能培训基础课程',
|
||||
coverImage: '/static/icon/server1.png',
|
||||
videoUrl: ''
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: '面试技巧分享',
|
||||
coverImage: '/static/icon/server2.png',
|
||||
videoUrl: ''
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: '简历制作指南',
|
||||
coverImage: '/static/icon/server3.png',
|
||||
videoUrl: ''
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
title: '职场沟通技巧',
|
||||
coverImage: '/static/icon/server4.png',
|
||||
videoUrl: ''
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
title: '职业规划讲座',
|
||||
coverImage: '/static/icon/flame.png',
|
||||
videoUrl: ''
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
title: '行业趋势分析',
|
||||
coverImage: '/static/icon/flame2.png',
|
||||
videoUrl: ''
|
||||
}
|
||||
];
|
||||
|
||||
onLoad(() => {
|
||||
getDataList('refresh');
|
||||
});
|
||||
|
||||
// 搜索视频
|
||||
function searchVideo() {
|
||||
getDataList('refresh');
|
||||
}
|
||||
|
||||
// 清除搜索内容
|
||||
function clearSearch() {
|
||||
searchKeyword.value = '';
|
||||
getDataList('refresh');
|
||||
}
|
||||
|
||||
// 获取视频列表
|
||||
function getDataList(type = 'add') {
|
||||
if (type === 'refresh') {
|
||||
pageState.page = 1;
|
||||
pageState.maxPage = 1;
|
||||
}
|
||||
if (type === 'add' && pageState.page < pageState.maxPage) {
|
||||
pageState.page += 1;
|
||||
}
|
||||
|
||||
// 模拟API请求延迟
|
||||
setTimeout(() => {
|
||||
// 在实际项目中,这里应该调用真实的API接口
|
||||
// 目前使用模拟数据
|
||||
let filteredList = [...mockVideoData];
|
||||
|
||||
// 如果有搜索关键词,进行过滤
|
||||
if (searchKeyword.value.trim()) {
|
||||
filteredList = filteredList.filter(video =>
|
||||
video.title.toLowerCase().includes(searchKeyword.value.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
const start = 0;
|
||||
const end = pageState.pageSize;
|
||||
const pageData = filteredList.slice(start, end);
|
||||
|
||||
if (type === 'add') {
|
||||
pageState.list = [...pageState.list, ...pageData];
|
||||
} else {
|
||||
pageState.list = pageData;
|
||||
}
|
||||
|
||||
pageState.total = filteredList.length;
|
||||
pageState.maxPage = Math.ceil(pageState.total / pageState.pageSize);
|
||||
}, 300);
|
||||
}
|
||||
|
||||
// 播放视频
|
||||
function playVideo(video) {
|
||||
// 在实际项目中,这里应该导航到视频播放页面
|
||||
// 或者调用视频播放组件
|
||||
console.log('播放视频:', video.title);
|
||||
// 示例:navTo(`/pages/videoPlayer/videoPlayer?id=${video.id}`);
|
||||
$api.msg(`准备播放: ${video.title}`);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.btnback{
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
}
|
||||
.btn {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
}
|
||||
image {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.collection-search{
|
||||
padding: 10rpx 20rpx;
|
||||
.search-content{
|
||||
position: relative
|
||||
display: flex
|
||||
align-items: center
|
||||
padding: 14rpx 0
|
||||
.header-input{
|
||||
padding: 0
|
||||
width: calc(100%);
|
||||
position: relative
|
||||
.iconsearch{
|
||||
position: absolute
|
||||
left: 30rpx;
|
||||
top: 50%
|
||||
transform: translate(0, -50%)
|
||||
z-index: 1
|
||||
}
|
||||
.input{
|
||||
padding: 0 80rpx 0 80rpx
|
||||
height: 80rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 75rpx 75rpx 75rpx 75rpx;
|
||||
border: 2rpx solid #ECECEC
|
||||
font-size: 28rpx;
|
||||
}
|
||||
.clear-icon{
|
||||
position: absolute
|
||||
right: 30rpx;
|
||||
top: 50%
|
||||
transform: translate(0, -50%)
|
||||
z-index: 1
|
||||
cursor: pointer
|
||||
}
|
||||
.inputplace{
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #B5B5B5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.main-list{
|
||||
background-color: #ffffff;
|
||||
padding: 20rpx 20rpx 28rpx 20rpx;
|
||||
margin:10rpx 30rpx ;
|
||||
box-shadow: 0px 3px 20px 0px rgba(0,105,234,0.1);
|
||||
border-radius: 12px;
|
||||
}
|
||||
.list-title{
|
||||
font-weight: bold;
|
||||
font-size: 36rpx;
|
||||
color: #404040;
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.title-line{
|
||||
position: absolute;
|
||||
bottom: -10rpx;
|
||||
left: 36rpx;
|
||||
width: 70rpx;
|
||||
height: 8rpx;
|
||||
background: linear-gradient(90deg, #FFAD58 0%, #FF7A5B 100%);
|
||||
border-radius: 2px;
|
||||
}
|
||||
.video-grid{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20rpx;
|
||||
}
|
||||
.video-item{
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
transition: transform 0.2s;
|
||||
padding: 20rpx
|
||||
}
|
||||
.video-item:active{
|
||||
transform: scale(0.98);
|
||||
}
|
||||
.video-cover{
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-top: 56.25%; /* 16:9 比例 */
|
||||
background: #f0f0f0;
|
||||
border-radius: 4rpx;
|
||||
}
|
||||
.video-cover image{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.video-info{
|
||||
padding: 16rpx 16rpx 0 16rpx;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
.video-title{
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
line-height: 40rpx;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
@@ -1,316 +0,0 @@
|
||||
<template>
|
||||
<AppLayout :title="title" :show-bg-image="false" @onScrollBottom="getDataList('add')">
|
||||
<!-- <template #headerleft>
|
||||
<view class="btnback">
|
||||
<image src="@/static/icon/back.png" @click="navBack"></image>
|
||||
</view>
|
||||
</template> -->
|
||||
<template #headContent>
|
||||
<view class="collection-search">
|
||||
<view class="search-content">
|
||||
<view class="header-input button-click">
|
||||
<uni-icons class="iconsearch" color="#6A6A6A" type="search" size="22"></uni-icons>
|
||||
<input
|
||||
class="input"
|
||||
v-model="searchKeyword"
|
||||
@confirm="searchVideo"
|
||||
placeholder="输入视频名称"
|
||||
placeholder-class="inputplace"
|
||||
/>
|
||||
<uni-icons
|
||||
v-if="searchKeyword"
|
||||
class="clear-icon"
|
||||
type="clear"
|
||||
size="24"
|
||||
color="#999"
|
||||
@click="clearSearch"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<view class="main-list">
|
||||
<view class="list-title">
|
||||
<text>视频列表</text>
|
||||
<view class="title-line"></view>
|
||||
</view>
|
||||
<view class="video-grid" v-if="pageState.list.length">
|
||||
<view
|
||||
v-for="video in pageState.list"
|
||||
:key="video.id || video.videoId"
|
||||
class="video-item"
|
||||
:style="getItemBackgroundStyle('video-bg.png')"
|
||||
@click="playVideo(video)"
|
||||
>
|
||||
<view class="video-cover">
|
||||
<image
|
||||
:src="video.coverImage || video.videoCover || '/static/icon/video.png'"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
</view>
|
||||
<view class="video-info">
|
||||
{{ video.title || video.videoName || '未命名视频' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<empty v-else pdTop="200"></empty>
|
||||
</view>
|
||||
</AppLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject, ref, reactive } from 'vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
const { $api, navTo, navBack } = inject('globalFunction');
|
||||
|
||||
// state
|
||||
const title = ref('');
|
||||
const searchKeyword = ref('');
|
||||
const pageState = reactive({
|
||||
page: 0,
|
||||
list: [],
|
||||
total: 0,
|
||||
maxPage: 1,
|
||||
pageSize: 12,
|
||||
search: {},
|
||||
});
|
||||
const baseUrl = 'http://10.110.145.145/images/train/';
|
||||
const getItemBackgroundStyle = (imageName) => ({
|
||||
backgroundImage: `url(${baseUrl + imageName})`,
|
||||
backgroundSize: 'cover', // 覆盖整个容器
|
||||
backgroundPosition: 'center', // 居中
|
||||
backgroundRepeat: 'no-repeat'
|
||||
});
|
||||
// 模拟视频数据
|
||||
const mockVideoData = [
|
||||
{
|
||||
id: '1',
|
||||
title: '职业技能培训基础课程',
|
||||
coverImage: '/static/icon/server1.png',
|
||||
videoUrl: ''
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: '面试技巧分享',
|
||||
coverImage: '/static/icon/server2.png',
|
||||
videoUrl: ''
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: '简历制作指南',
|
||||
coverImage: '/static/icon/server3.png',
|
||||
videoUrl: ''
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
title: '职场沟通技巧',
|
||||
coverImage: '/static/icon/server4.png',
|
||||
videoUrl: ''
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
title: '职业规划讲座',
|
||||
coverImage: '/static/icon/flame.png',
|
||||
videoUrl: ''
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
title: '行业趋势分析',
|
||||
coverImage: '/static/icon/flame2.png',
|
||||
videoUrl: ''
|
||||
}
|
||||
];
|
||||
|
||||
onLoad(() => {
|
||||
getDataList('refresh');
|
||||
});
|
||||
|
||||
// 搜索视频
|
||||
function searchVideo() {
|
||||
getDataList('refresh');
|
||||
}
|
||||
|
||||
// 清除搜索内容
|
||||
function clearSearch() {
|
||||
searchKeyword.value = '';
|
||||
getDataList('refresh');
|
||||
}
|
||||
|
||||
// 获取视频列表
|
||||
function getDataList(type = 'add') {
|
||||
if (type === 'refresh') {
|
||||
pageState.page = 1;
|
||||
pageState.maxPage = 1;
|
||||
}
|
||||
if (type === 'add' && pageState.page < pageState.maxPage) {
|
||||
pageState.page += 1;
|
||||
}
|
||||
|
||||
// 模拟API请求延迟
|
||||
setTimeout(() => {
|
||||
// 在实际项目中,这里应该调用真实的API接口
|
||||
// 目前使用模拟数据
|
||||
let filteredList = [...mockVideoData];
|
||||
|
||||
// 如果有搜索关键词,进行过滤
|
||||
if (searchKeyword.value.trim()) {
|
||||
filteredList = filteredList.filter(video =>
|
||||
video.title.toLowerCase().includes(searchKeyword.value.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
const start = 0;
|
||||
const end = pageState.pageSize;
|
||||
const pageData = filteredList.slice(start, end);
|
||||
|
||||
if (type === 'add') {
|
||||
pageState.list = [...pageState.list, ...pageData];
|
||||
} else {
|
||||
pageState.list = pageData;
|
||||
}
|
||||
|
||||
pageState.total = filteredList.length;
|
||||
pageState.maxPage = Math.ceil(pageState.total / pageState.pageSize);
|
||||
}, 300);
|
||||
}
|
||||
|
||||
// 播放视频
|
||||
function playVideo(video) {
|
||||
// 在实际项目中,这里应该导航到视频播放页面
|
||||
// 或者调用视频播放组件
|
||||
console.log('播放视频:', video.title);
|
||||
// 示例:navTo(`/pages/videoPlayer/videoPlayer?id=${video.id}`);
|
||||
$api.msg(`准备播放: ${video.title}`);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.btnback{
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
}
|
||||
.btn {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
}
|
||||
image {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.collection-search{
|
||||
padding: 10rpx 20rpx;
|
||||
.search-content{
|
||||
position: relative
|
||||
display: flex
|
||||
align-items: center
|
||||
padding: 14rpx 0
|
||||
.header-input{
|
||||
padding: 0
|
||||
width: calc(100%);
|
||||
position: relative
|
||||
.iconsearch{
|
||||
position: absolute
|
||||
left: 30rpx;
|
||||
top: 50%
|
||||
transform: translate(0, -50%)
|
||||
z-index: 1
|
||||
}
|
||||
.input{
|
||||
padding: 0 80rpx 0 80rpx
|
||||
height: 80rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 75rpx 75rpx 75rpx 75rpx;
|
||||
border: 2rpx solid #ECECEC
|
||||
font-size: 28rpx;
|
||||
}
|
||||
.clear-icon{
|
||||
position: absolute
|
||||
right: 30rpx;
|
||||
top: 50%
|
||||
transform: translate(0, -50%)
|
||||
z-index: 1
|
||||
cursor: pointer
|
||||
}
|
||||
.inputplace{
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #B5B5B5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.main-list{
|
||||
background-color: #ffffff;
|
||||
padding: 20rpx 20rpx 28rpx 20rpx;
|
||||
margin:10rpx 30rpx ;
|
||||
box-shadow: 0px 3px 20px 0px rgba(0,105,234,0.1);
|
||||
border-radius: 12px;
|
||||
}
|
||||
.list-title{
|
||||
font-weight: bold;
|
||||
font-size: 36rpx;
|
||||
color: #404040;
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.title-line{
|
||||
position: absolute;
|
||||
bottom: -10rpx;
|
||||
left: 36rpx;
|
||||
width: 70rpx;
|
||||
height: 8rpx;
|
||||
background: linear-gradient(90deg, #FFAD58 0%, #FF7A5B 100%);
|
||||
border-radius: 2px;
|
||||
}
|
||||
.video-grid{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20rpx;
|
||||
}
|
||||
.video-item{
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
transition: transform 0.2s;
|
||||
padding: 20rpx
|
||||
}
|
||||
.video-item:active{
|
||||
transform: scale(0.98);
|
||||
}
|
||||
.video-cover{
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-top: 56.25%; /* 16:9 比例 */
|
||||
background: #f0f0f0;
|
||||
border-radius: 4rpx;
|
||||
}
|
||||
.video-cover image{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.video-info{
|
||||
padding: 16rpx 16rpx 0 16rpx;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
.video-title{
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
line-height: 40rpx;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
@@ -1,514 +0,0 @@
|
||||
// 获取人员基本信息详情
|
||||
|
||||
|
||||
|
||||
// import { post, get } from '../../utils/request.js'
|
||||
|
||||
// export function getPersonInfo(id) {
|
||||
// return get({
|
||||
// url: `personnel/personBaseInfo/${id}`,
|
||||
// method: 'get'
|
||||
// })
|
||||
// }
|
||||
|
||||
import request from '@/utilsRc/request'
|
||||
|
||||
// 根据 userId 获取企业详情
|
||||
export function companyDetails(userId) {
|
||||
return request({
|
||||
method: 'get',
|
||||
url: `/company/unitBaseInfo/user/${userId}`,
|
||||
})
|
||||
}
|
||||
|
||||
// 企业-推荐人员信息
|
||||
export function recommendedPerson(params) {
|
||||
return request({
|
||||
url: '/company/unitBaseInfo/recommend/person',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 人员邀请
|
||||
export function invitePerson(params) {
|
||||
return request({
|
||||
url: '/company/unitBaseInfo/invite',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 获取企业招聘岗位列表
|
||||
export function jobList(params) {
|
||||
return request({
|
||||
url: '/company/unitPostInfo/list',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 查找已投递、已推荐、已邀请的人员信息
|
||||
export function listMatch(query) {
|
||||
return request({
|
||||
url: '/company/unitBaseInfo/relevance',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 添加企业基本信息
|
||||
export function addJobBase(data) {
|
||||
return request({
|
||||
url: '/company/unitBaseInfo',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询部门下拉树结构
|
||||
export function deptTreeSelect() {
|
||||
return request({
|
||||
url: '/system/center/user/deptTree',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 企业发布招聘岗位
|
||||
export function addJob(data) {
|
||||
return request({
|
||||
url: '/company/unitPostInfo',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取招聘工种列表
|
||||
export function jobTypeList(params) {
|
||||
return request({
|
||||
url: '/basicdata/workType/list',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 企业基本信息列表
|
||||
export function jobBaseList(query) {
|
||||
return request({
|
||||
url: '/company/unitBaseInfo/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 获取企业招聘岗位信息详细信息
|
||||
export function getJob(id) {
|
||||
return request({
|
||||
url: `/company/unitPostInfo/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
// 修改企业招聘岗位信息
|
||||
export function updateJob(data) {
|
||||
return request({
|
||||
url: '/company/unitPostInfo',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改企业基本信息
|
||||
export function updateJobBase(data) {
|
||||
return request({
|
||||
url: '/company/unitBaseInfo',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 查询角色详细
|
||||
export function getJobService(id) {
|
||||
return request({
|
||||
url: '/personnel/personBaseInfo/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询推荐人员、已推荐、已邀请 详情
|
||||
export function getUnitBaseInfo(id) {
|
||||
return request({
|
||||
url: '/manage/personDemand/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 查询工种列表
|
||||
export function listJobType(query) {
|
||||
return request({
|
||||
url: '/basicdata/workType/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 人员基本信息 - 列表
|
||||
export function personInfoList(query) {
|
||||
return request({
|
||||
url: '/personnel/personBaseInfo/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 获取人员基本信息详情
|
||||
export function getPersonInfo(id) {
|
||||
return request({
|
||||
url: `/personnel/personBaseInfo/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 删除人员基本信息
|
||||
export function delPersonInfo(ids) {
|
||||
return request({
|
||||
url: '/personnel/personBaseInfo/' + ids,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 删除录入人员 公用 type = 1 失业中 2 就业困难 3 离校生 4 其他人员
|
||||
export function delPersonUser(ids) {
|
||||
return request({
|
||||
url: `/personnel/personInputInfo/${ids}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
|
||||
// 新增人员基本信息
|
||||
export function addPersonInfo(data) {
|
||||
return request({
|
||||
url: '/personnel/personBaseInfo',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改人员基本信息
|
||||
export function updatePersonInfo(data) {
|
||||
return request({
|
||||
url: '/personnel/personBaseInfo',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
//社区人员审核
|
||||
export function personInfoAudit(data) {
|
||||
return request({
|
||||
url: '/personnel/personBaseInfo/audit',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
//记录查看身份证
|
||||
export function recordLookIdCard(params) {
|
||||
return request({
|
||||
url: '/personnel/personBaseInfo/recordLookIdCard',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* 失业人员 --------------------------------------------- start */
|
||||
|
||||
// 新增失业人员
|
||||
export function addPersonUnemployed(data) {
|
||||
return request({
|
||||
url: '/person/unemployment',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 失业人员修改
|
||||
export function updatePersonUnemployed(data) {
|
||||
return request({
|
||||
url: '/person/unemployment',
|
||||
method: 'put',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 失业人员列表
|
||||
export function unemployment(params) {
|
||||
return request({
|
||||
url: '/person/unemployment/list',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 失业人员详情
|
||||
export function unemploymentDetails(id) {
|
||||
return request({
|
||||
url: `/person/unemployment/${id}`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// 失业人员删除
|
||||
export function unemploymentDelete(id) {
|
||||
return request({
|
||||
url: `/person/unemployment/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
|
||||
/* 失业人员 --------------------------------------------- end */
|
||||
|
||||
|
||||
/* 就业困难人员 --------------------------------------------- start */
|
||||
|
||||
// 新增就业困难
|
||||
export function addPersonDifficult(data) {
|
||||
return request({
|
||||
url: '/person/findingEmployment',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改就业困难
|
||||
export function updatePersonDifficult(data) {
|
||||
return request({
|
||||
url: '/person/findingEmployment',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 就业困难列表
|
||||
export function findingEmployment(params) {
|
||||
return request({
|
||||
url: '/person/findingEmployment/list',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 就业困难详情
|
||||
export function findingEmploymentDetails(id) {
|
||||
return request({
|
||||
url: `/person/findingEmployment/${id}`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// 就业困难删除
|
||||
export function findingEmploymentDelete(id) {
|
||||
return request({
|
||||
url: `/person/findingEmployment/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
/* 就业困难人员 --------------------------------------------- end */
|
||||
|
||||
|
||||
/* 离校未就业高校生 --------------------------------------------- start */
|
||||
|
||||
// 新增离校未就业高校生
|
||||
export function addLeaveSchool(data) {
|
||||
return request({
|
||||
url: '/person/leavingSchoolInfo',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改离校未就业高校生
|
||||
export function updateLeaveSchool(data) {
|
||||
return request({
|
||||
url: '/person/leavingSchoolInfo',
|
||||
method: 'put',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 高校未就业列表
|
||||
export function leavingSchoolInfo(params) {
|
||||
return request({
|
||||
url: '/person/leavingSchoolInfo/list',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 高校未就业详情
|
||||
export function leavingSchoolInfoDetails(id) {
|
||||
return request({
|
||||
url: `/person/leavingSchoolInfo/${id}`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 高校未就业删除
|
||||
export function leavingSchoolInfoDelete(id) {
|
||||
return request({
|
||||
url: `/person/leavingSchoolInfo/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/* 离校未就业高校生 --------------------------------------------- end */
|
||||
|
||||
/* 其他人员 --------------------------------------------- start */
|
||||
|
||||
// 新增其他人员
|
||||
export function addOther(data) {
|
||||
return request({
|
||||
url: '/person/other',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 其他人员修改
|
||||
export function updateOther(data) {
|
||||
return request({
|
||||
url: '/person/other',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 其他人员列表
|
||||
export function other(params) {
|
||||
return request({
|
||||
url: '/person/other/list',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 其他人员详情
|
||||
export function otherDetails(id) {
|
||||
return request({
|
||||
url: `/person/other/${id}`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// 其他人员删除
|
||||
export function otherDelete(id) {
|
||||
return request({
|
||||
url: `/person/other/${id}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
/* 其他人员 --------------------------------------------- end */
|
||||
|
||||
// 需求预警列表
|
||||
export function personAlertList(params) {
|
||||
return request({
|
||||
url: '/manage/personDemand/warningList',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
export function personDealList(params) {
|
||||
return request({
|
||||
url: '/manage/personDemand/dealingList',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
// 服务追踪 服务类型/服务id
|
||||
export function serviceTraceability({
|
||||
demandType,
|
||||
id
|
||||
}) {
|
||||
return request({
|
||||
// url: `/system/personRequirementsRecords/serviceTraceability/${demandType}/${id}`,
|
||||
url: `/timelime/timelime/fwzs/${id}`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// 需求办结
|
||||
export function requirementCompletion(url, data) {
|
||||
return request({
|
||||
url,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
//岗位审核
|
||||
export function jobAudit(data) {
|
||||
return request({
|
||||
url: '/company/unitPostInfo/audit',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
//社群 首页未完成数
|
||||
// export function getPeopleCount() {
|
||||
// return request({
|
||||
// url: '/pc/index/getPeopleCount',
|
||||
// method: 'get',
|
||||
// })
|
||||
// }
|
||||
|
||||
//社群 首页未完成数
|
||||
export function getDemandUnfinished() {
|
||||
return request({
|
||||
url: '/pc/index/todo',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 删除企业招聘岗位信息
|
||||
export function delJob(ids) {
|
||||
return request({
|
||||
url: '/company/unitPostInfo/' + ids,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 所在社区列表
|
||||
export function deptList(params) {
|
||||
return request({
|
||||
'url': `/system/center/user/deptList`,
|
||||
'method': 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 所在社区列表
|
||||
export function returnPerson(params) {
|
||||
return request({
|
||||
'url': `/personnel/personBaseInfo/returnPerson`,
|
||||
'method': 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
// 根据人的身份证查询人的详细信息
|
||||
export function getIdNumberInfo(params) {
|
||||
return request({
|
||||
'url': `/personnel/personBaseInfo/getIdNumberInfo`,
|
||||
'method': 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { post, get } from '../../utils/request.js'
|
||||
|
||||
export function listJobType(query) {
|
||||
return get({
|
||||
url: 'basicdata/workType/list',
|
||||
|
||||
params: query
|
||||
})
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
import { post, get } from '../utils/request.js'
|
||||
|
||||
// 登录方法
|
||||
export function login(data) {
|
||||
return post({
|
||||
url: 'personnel/personBaseInfo/loginGrAndQy',
|
||||
data,
|
||||
headers: {
|
||||
isToken: false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取验证码
|
||||
export function getCodeImg() {
|
||||
return get({
|
||||
url: 'captchaImage',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
timeout: 20000
|
||||
})
|
||||
}
|
||||
|
||||
// 获取用户详细信息
|
||||
export function getInfo() {
|
||||
return get({
|
||||
url: '/getInfo'
|
||||
})
|
||||
}
|
||||
|
||||
// 退出方法
|
||||
export function logout() {
|
||||
return post({
|
||||
url: '/logout'
|
||||
})
|
||||
}
|
||||
|
||||
// 短信验证码
|
||||
export function getCodeSms() {
|
||||
return get({
|
||||
url: '/captchaSms',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
timeout: 20000
|
||||
})
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
// 人员接口
|
||||
import { post, get } from '../../utils/request.js'
|
||||
export function getPersonBase(params) {
|
||||
return get({
|
||||
url: 'personnel/personBaseInfo/list',
|
||||
|
||||
params
|
||||
})
|
||||
}
|
||||
export function getPersonList(params) {
|
||||
return get({
|
||||
url: 'personnel/personBaseInfo/list',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 新增角色
|
||||
export function addInvestigate(data) {
|
||||
return post({
|
||||
// url: '/process/processInterview',
|
||||
url: '/timelime/timelime',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
// 查询个人需求信息列表
|
||||
import { post, get } from '../../utils/request.js'
|
||||
export function listPersonDemand(query) {
|
||||
return get({
|
||||
url: 'manage/personDemand/list',
|
||||
|
||||
params: query
|
||||
})
|
||||
}
|
||||
export function delPersonDemand(id) {
|
||||
return get({
|
||||
url: 'manage/personDemand/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 查询个人需求信息详细
|
||||
export function getPersonDemand(id) {
|
||||
return get({
|
||||
url: 'manage/personDemand/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增个人需求信息
|
||||
export function addPersonDemand(data) {
|
||||
return post({
|
||||
url: 'manage/personDemand',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改个人需求信息
|
||||
export function updatePersonDemand(data) {
|
||||
return post({
|
||||
url: 'manage/personDemand',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import { post, get } from '../../utils/request.js'
|
||||
|
||||
// 登录方法
|
||||
export function personInfoList(data) {
|
||||
return get({
|
||||
url: 'personnel/personBaseInfo/list',
|
||||
params: data,
|
||||
})
|
||||
}
|
||||
// 需求预警列表
|
||||
export function personAlertList(params) {
|
||||
return get({
|
||||
url: 'manage/personDemand/warningList',
|
||||
|
||||
params
|
||||
})
|
||||
}
|
||||
//经办人数据获取
|
||||
export function getJbrInfo() {
|
||||
return get({
|
||||
url: `system/center/user/selectHxjbr`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
@@ -1,374 +0,0 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="popupAll">
|
||||
<view class="tabList dispalyF" v-if="!allCheckShow">
|
||||
<scroll-view scroll-x style="white-space: nowrap;width: calc(100% - 144rpx);margin-left: 32rpx">
|
||||
<view class="rightView">
|
||||
<view
|
||||
@click="getTopActive(newCkeckData[0], index, item)"
|
||||
:class="
|
||||
index == newCkeckData[0].activeIndex
|
||||
? 'popupItem-active tabItem rightView'
|
||||
: 'popupItem tabItem rightView'
|
||||
"
|
||||
v-for="(item, index) in newCkeckData[0] && newCkeckData[0].data"
|
||||
:key="index"
|
||||
style="display: inline-block; margin-right: 15rpx;margin-bottom: 0"
|
||||
>
|
||||
{{ item.dictLabel }}
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="tabrightBtnOut" @click="allCheckShowChange">
|
||||
<image
|
||||
class="tabrightBtn"
|
||||
src="https://rc.jinan.gov.cn/qcwjyH5/static/images/getMoreCheck.png"
|
||||
mode=""
|
||||
></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="popupListAll" v-if="allCheckShow">
|
||||
<view
|
||||
class="popupList"
|
||||
v-for="(item, index) in newCkeckData"
|
||||
:key="index"
|
||||
>
|
||||
<view class="tabTitle">
|
||||
{{ item.name }}
|
||||
</view>
|
||||
<view
|
||||
class="dispalyF"
|
||||
:style="[
|
||||
{
|
||||
position: 'sticky',
|
||||
top: '-2rpx',
|
||||
zIndex: 1,
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<template>
|
||||
<template v-for="(itm, idx) in item.data">
|
||||
<view :key="idx" v-if="!itm.mode"
|
||||
@click="getActive(itm, index, idx)"
|
||||
:class="
|
||||
idx == item.activeIndex
|
||||
? 'popupItem-active'
|
||||
: 'popupItem'
|
||||
"
|
||||
style="margin-right: 20rpx"
|
||||
>
|
||||
{{ itm.dictLabel }}
|
||||
</view>
|
||||
<!-- <view v-if="itm.mode == 'timerange'">
|
||||
{{itm.start||'开始时间'}} - {{ item.end||'结束时间' }}
|
||||
</view> -->
|
||||
</template>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="search_btn" @click="search">
|
||||
查询
|
||||
</view> -->
|
||||
<view class="bottom-search">
|
||||
<view class="search-left" @click="clearAll">
|
||||
清空
|
||||
</view>
|
||||
<view class="search-right" @click="search">查询</view>
|
||||
</view>
|
||||
<view class="popupPic" @click="close">
|
||||
<u-icon name="arrow-up"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="gropo" v-if="allCheckShow"></view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "popupList",
|
||||
props: {
|
||||
checkData: {
|
||||
type: Array,
|
||||
default: [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
newCkeckData: this.checkData,
|
||||
allCheckShow: false,
|
||||
params: "",
|
||||
fijItem: "",
|
||||
outData: [],
|
||||
nickName: ''
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
checkData(newVal) {
|
||||
this.newCkeckData = newVal;
|
||||
},
|
||||
allCheckShow(newVal) {
|
||||
this.$parent.zctopShow = newVal;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (uni.getStorageSync("userInfo")) {
|
||||
let userInfo = JSON.parse(uni.getStorageSync("userInfo"));
|
||||
this.nickName = userInfo.userName ? userInfo.userName : "";
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
allCheckShowChange() {
|
||||
this.allCheckShow = !this.allCheckShow;
|
||||
},
|
||||
search() {
|
||||
this.allCheckShow = false;
|
||||
let param = [];
|
||||
this.$emit('popupSearch', this.newCkeckData)
|
||||
},
|
||||
clearAll(){
|
||||
this.allCheckShow = false;
|
||||
this.newCkeckData.forEach((item, index) => {
|
||||
item.activeIndex=0
|
||||
});
|
||||
this.$emit('popupSearch', this.newCkeckData)
|
||||
},
|
||||
close() {
|
||||
this.allCheckShow = false;
|
||||
},
|
||||
getActive(item, index, idx) {
|
||||
if(item.checkLogin&&!this.nickName){
|
||||
uni.showToast({
|
||||
title: item.noLoginText,
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
this.newCkeckData[index].activeIndex = idx;
|
||||
},
|
||||
getTopActive(list, index, item) {
|
||||
if(item.checkLogin&&!this.nickName){
|
||||
uni.showToast({
|
||||
title: item.noLoginText,
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
this.$set(this.newCkeckData[0], "activeIndex", index);
|
||||
this.search();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.dispalyF {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.popupAll {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
.popupListAll {
|
||||
background: #F2F4F7;
|
||||
position: relative;
|
||||
z-index: 99;
|
||||
width: 100%;
|
||||
padding-top: 8rpx;
|
||||
}
|
||||
|
||||
.popupList {
|
||||
background: #F2F4F7;
|
||||
margin: 0 32rpx;
|
||||
|
||||
.tabTitle {
|
||||
font-family: Source Han Sans;
|
||||
font-size: 24rpx;
|
||||
color: #282828;
|
||||
margin: 16rpx 0;
|
||||
}
|
||||
|
||||
.popupItem {
|
||||
padding: 0 32rpx;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
border-radius: 4rpx;
|
||||
font-family: Source Han Sans;
|
||||
font-size: 24rpx;
|
||||
font-feature-settings: "kern" on;
|
||||
color: #282828;
|
||||
margin-right: 15rpx;
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 20rpx;
|
||||
line-height: 56rpx;
|
||||
min-width: 144rpx;
|
||||
border: 1rpx solid #CAD4E2;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.popupItem-active {
|
||||
padding: 0 32rpx;
|
||||
text-align: center;
|
||||
background: #1A62CE;
|
||||
border-radius: 4rpx;
|
||||
font-family: Source Han Sans;
|
||||
font-size: 24rpx;
|
||||
font-feature-settings: "kern" on;
|
||||
color: #fff;
|
||||
margin-right: 15rpx;
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 20rpx;
|
||||
line-height: 56rpx;
|
||||
min-width: 144rpx;
|
||||
border: 1rpx solid #1A62CE;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.popupPic {
|
||||
width: 100%;
|
||||
padding-top: 16rpx;
|
||||
padding-bottom: 28rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.gropo {
|
||||
position: fixed;
|
||||
left: 0rpx;
|
||||
top: 0rpx;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
opacity: 0.6;
|
||||
background: rgba(0, 0, 0, 0.23);
|
||||
}
|
||||
|
||||
.tabList {
|
||||
padding: 32rpx 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
flex-wrap: nowrap;
|
||||
width: 100%;
|
||||
z-index: 9;
|
||||
|
||||
.tabItem {
|
||||
line-height: 56rpx;
|
||||
padding: 0 32rpx;
|
||||
background: #fff;
|
||||
border-radius: 4rpx;
|
||||
font-family: Source Han Sans;
|
||||
font-size: 24rpx;
|
||||
font-feature-settings: "kern" on;
|
||||
color: #282828;
|
||||
margin-right: 15rpx;
|
||||
flex-shrink: 0;
|
||||
min-width: 144rpx;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tabrightBtnOut {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 87rpx;
|
||||
height: 56rpx;
|
||||
// background: linear-gradient(270deg, #2A51DF 0%, rgba(66, 110, 230, 0) 100%);
|
||||
}
|
||||
|
||||
.tabrightBtn {
|
||||
position: absolute;
|
||||
right: 36rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
z-index: 99;
|
||||
}
|
||||
.popupItem {
|
||||
padding: 0 32rpx;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
border-radius: 4rpx;
|
||||
font-family: Source Han Sans;
|
||||
font-size: 24rpx;
|
||||
font-feature-settings: "kern" on;
|
||||
color: #282828;
|
||||
margin-right: 15rpx;
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 20rpx;
|
||||
line-height: 56rpx;
|
||||
min-width: 144rpx;
|
||||
border: 1rpx solid #CAD4E2;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.popupItem-active {
|
||||
padding: 0 32rpx;
|
||||
text-align: center;
|
||||
background: #1A62CE;
|
||||
border-radius: 4rpx;
|
||||
font-family: Source Han Sans;
|
||||
font-size: 24rpx;
|
||||
font-feature-settings: "kern" on;
|
||||
color: #fff;
|
||||
margin-right: 15rpx;
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 20rpx;
|
||||
line-height: 56rpx;
|
||||
min-width: 144rpx;
|
||||
border: 1rpx solid #1A62CE;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.dispalyF {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.rightView :last-child {
|
||||
margin-right: 100rpx !important;
|
||||
}
|
||||
|
||||
.bottom-search {
|
||||
margin-top:56rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 16rpx 32rpx 0;
|
||||
.search-left {
|
||||
font-family: Source Han Sans;
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
line-height: 70rpx;
|
||||
background: #fff;
|
||||
color: #3D3D3D;
|
||||
border-radius: 4rpx;
|
||||
height: 62rpx;
|
||||
flex-grow: 1;
|
||||
text-align: center;
|
||||
}
|
||||
.search-right {
|
||||
margin-right: 30rpx;
|
||||
width: 472rpx;
|
||||
line-height: 64rpx;
|
||||
border-radius: 4rpx;
|
||||
background: linear-gradient(270deg, #53A0EA 10%, #1A62CE 100%);
|
||||
font-family: Source Han Sans;
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
margin-left: 24rpx;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,130 +0,0 @@
|
||||
<template>
|
||||
<u-popup :show="showExitPopup" @close="closeExitPopup" closeOnClickOverlay>
|
||||
<view class="exit_popup df_flex df__direction_column ">
|
||||
<view class="popup-header">
|
||||
<u-icon name="warning" size="60rpx" color="#E33C3C"></u-icon>
|
||||
<view class="popup-title">退出登录</view>
|
||||
</view>
|
||||
<view class="df_flex df_justify_center name">{{nick || '--'}}</view>
|
||||
<view class="popup-content">确定要退出当前账号吗?</view>
|
||||
<view class="df_flex">
|
||||
<u-button class="custom_btn" text="退出登录" @tap="logOut"></u-button>
|
||||
<u-button class="cancel_btn" text="取 消" @tap="closeExitPopup"></u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import {
|
||||
// mapGetters
|
||||
// } from 'vuex'
|
||||
export default {
|
||||
name: "exitPopup",
|
||||
// computed: {
|
||||
// ...mapGetters(['showExitPopup', 'nick'])
|
||||
// },
|
||||
data() {
|
||||
return {
|
||||
showExitPopup: false,
|
||||
nick: ''
|
||||
}
|
||||
},
|
||||
onload() {
|
||||
this.nick = this.$store.state.user.nick
|
||||
this.showExitPopup = this.$store.state.user.showExitPopup
|
||||
},
|
||||
methods: {
|
||||
closeExitPopup() {
|
||||
this.$store.commit('SET_SHOWEXITPOPUP', false)
|
||||
},
|
||||
// 退出
|
||||
logOut() {
|
||||
this.$store.dispatch('LogOut').then((res) => {
|
||||
this.closeExitPopup()
|
||||
uni.navigateTo({
|
||||
url: '/pages/login/login-one'
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.exit_popup {
|
||||
padding: 48rpx 60rpx 60rpx 60rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 24rpx;
|
||||
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.12);
|
||||
|
||||
&>view {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.popup-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 32rpx;
|
||||
|
||||
.popup-title {
|
||||
margin-top: 16rpx;
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.popup-content {
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: #666666;
|
||||
margin-bottom: 48rpx;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.name {
|
||||
margin: 16rpx auto 32rpx auto;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.custom_btn {
|
||||
margin: 16rpx;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
border-radius: 12rpx;
|
||||
background: linear-gradient(135deg, #E33C3C 0%, #ff4757 100%);
|
||||
box-shadow: 0 4rpx 12rpx rgba(227, 60, 60, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 2rpx 8rpx rgba(227, 60, 60, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
.cancel_btn {
|
||||
margin: 16rpx;
|
||||
color: #666666;
|
||||
border: 2rpx solid #e5e5e5;
|
||||
border-radius: 12rpx;
|
||||
background-color: #ffffff;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* 此处可直接引用自己项目封装好的 axios 配合后端联调
|
||||
*/
|
||||
|
||||
import request from './../utils/axios' // 组件内部封装的axios
|
||||
// import request from "@/api/axios.js" //调用项目封装的axios
|
||||
|
||||
// 获取验证图片 以及token
|
||||
export function reqGet (data) {
|
||||
return request({
|
||||
url: `/captcha/get`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 滑动或者点选验证
|
||||
export function reqCheck (data) {
|
||||
return request({
|
||||
url: '/captcha/check',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
/**
|
||||
* @word 要加密的内容
|
||||
* @keyWord String 服务器随机返回的关键字
|
||||
* 简化的加密函数,替代crypto-js依赖
|
||||
* 注意:这是一个简化实现,生产环境建议使用标准加密库
|
||||
*/
|
||||
export function aesEncrypt (word, keyWord = 'XwKsGlMcdPMEhR1B') {
|
||||
// 简单的Base64编码作为替代
|
||||
try {
|
||||
const text = JSON.stringify({ data: word, key: keyWord.slice(0, 8) });
|
||||
return btoa(unescape(encodeURIComponent(text)));
|
||||
} catch (e) {
|
||||
console.error('Encryption error:', e);
|
||||
// 如果编码失败,返回原始数据的字符串形式
|
||||
return String(word);
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
// 导入项目配置 - 使用相对路径替代@符号
|
||||
import config from '../../../../config.js'
|
||||
|
||||
// 使用uni-app内置的网络请求API替代axios
|
||||
const service = {
|
||||
// 基础配置
|
||||
baseURL: config.baseUrl,
|
||||
timeout: 40000,
|
||||
|
||||
// request方法封装
|
||||
request(options = {}) {
|
||||
// 合并默认配置和传入配置
|
||||
const requestOptions = {
|
||||
url: options.url,
|
||||
method: options.method || 'GET',
|
||||
header: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'Content-Type': 'application/json; charset=UTF-8',
|
||||
...options.header
|
||||
},
|
||||
data: options.data,
|
||||
timeout: options.timeout || this.timeout
|
||||
}
|
||||
|
||||
// 处理baseURL
|
||||
if (requestOptions.url && !requestOptions.url.startsWith('http')) {
|
||||
requestOptions.url = this.baseURL + requestOptions.url
|
||||
}
|
||||
|
||||
// 返回Promise
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
...requestOptions,
|
||||
success: (res) => {
|
||||
// 模拟axios的响应拦截器
|
||||
const responseData = res.data || {};
|
||||
resolve(responseData);
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('Request failed:', error);
|
||||
reject(error);
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// GET快捷方法
|
||||
get(url, params = {}, options = {}) {
|
||||
return this.request({
|
||||
...options,
|
||||
url,
|
||||
method: 'GET',
|
||||
data: params
|
||||
})
|
||||
},
|
||||
|
||||
// POST快捷方法
|
||||
post(url, data = {}, options = {}) {
|
||||
return this.request({
|
||||
...options,
|
||||
url,
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default service
|
||||
@@ -1,38 +0,0 @@
|
||||
export function resetSize (vm) {
|
||||
var imgWidth, imgHeight, barWidth, barHeight // 图片的宽度、高度,移动条的宽度、高度
|
||||
|
||||
// 修复:使用window.innerWidth/innerHeight替代不存在的window.offsetWidth
|
||||
var parentWidth = vm.$el?.parentNode?.offsetWidth || window.innerWidth
|
||||
var parentHeight = vm.$el?.parentNode?.offsetHeight || window.innerHeight
|
||||
|
||||
// 修复:使用vm替代this来访问组件属性
|
||||
if (vm.imgSize && vm.imgSize.width && vm.imgSize.width.indexOf('%') !== -1) {
|
||||
imgWidth = parseInt(vm.imgSize.width) / 100 * parentWidth + 'px'
|
||||
} else {
|
||||
imgWidth = vm.imgSize?.width || '300px'
|
||||
}
|
||||
|
||||
if (vm.imgSize && vm.imgSize.height && vm.imgSize.height.indexOf('%') !== -1) {
|
||||
imgHeight = parseInt(vm.imgSize.height) / 100 * parentHeight + 'px'
|
||||
} else {
|
||||
imgHeight = vm.imgSize?.height || '150px'
|
||||
}
|
||||
|
||||
if (vm.barSize && vm.barSize.width && vm.barSize.width.indexOf('%') !== -1) {
|
||||
barWidth = parseInt(vm.barSize.width) / 100 * parentWidth + 'px'
|
||||
} else {
|
||||
barWidth = vm.barSize?.width || '300px'
|
||||
}
|
||||
|
||||
if (vm.barSize && vm.barSize.height && vm.barSize.height.indexOf('%') !== -1) {
|
||||
barHeight = parseInt(vm.barSize.height) / 100 * parentHeight + 'px'
|
||||
} else {
|
||||
barHeight = vm.barSize?.height || '40px'
|
||||
}
|
||||
|
||||
return { imgWidth: imgWidth, imgHeight: imgHeight, barWidth: barWidth, barHeight: barHeight }
|
||||
}
|
||||
|
||||
export const codeChars = [1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
|
||||
export const codeColor1 = ['#fffff0', '#f0ffff', '#f0fff0', '#fff0f0']
|
||||
export const codeColor2 = ['#FF0033', '#006699', '#993366', '#FF9900', '#66CC66', '#FF33CC']
|
||||
@@ -1,657 +0,0 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<u-navbar
|
||||
title="帮扶登记"
|
||||
:autoBack="true"
|
||||
bgColor="transparent"
|
||||
leftIconColor="#fff"
|
||||
:titleStyle="{ color: '#fff' }"
|
||||
></u-navbar>
|
||||
<view class="input-outer-part">
|
||||
<scroll-view scroll-y="true" style="height: calc(100vh - 100px)">
|
||||
<view class="inner">
|
||||
<div class="self-form">
|
||||
<view class="inner-part">
|
||||
<div class="form-item required">
|
||||
<label class="form-label">被帮扶对象</label>
|
||||
<input
|
||||
v-model="serviceForm.serviceObjectName"
|
||||
style="border: none; width: 100%; padding: 10px 0;"
|
||||
placeholder="请输入"
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-item required">
|
||||
<label class="form-label">帮扶方式</label>
|
||||
<div class="form-value" @click="showPicker('demandType')" :class="{ noValue: !serviceForm.demandType }">
|
||||
{{ getDemandTypeLabel(serviceForm.demandType) || "请选择" }}
|
||||
<span class="arrow-down">▼</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-item required" v-if="serviceForm.demandType == '4' || serviceForm.demandType == '5'">
|
||||
<label class="form-label">帮扶时间</label>
|
||||
<div class="form-value" @click="showPicker('practicalSolutionTime')" :class="{ noValue: !serviceForm.practicalSolutionTime }">
|
||||
{{ serviceForm.practicalSolutionTime || "请选择" }}
|
||||
<span class="arrow-down">▼</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-item required" v-if="serviceForm.demandType == '4' || serviceForm.demandType == '5'">
|
||||
<label class="form-label">经办人</label>
|
||||
<div class="form-select-wrapper">
|
||||
<el-select
|
||||
style="width: 100%"
|
||||
v-model="serviceForm.agentUserId"
|
||||
placeholder="请选择经办人"
|
||||
@change="handleAgentChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in jingbrList1"
|
||||
:key="item.userId"
|
||||
:label="item.nickName"
|
||||
:value="item.userId"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-item required" v-if="serviceForm.demandType == '5'">
|
||||
<label class="form-label">电话沟通结果</label>
|
||||
<div class="form-value" @click="showPicker('dhgtjg')" :class="{ noValue: !serviceForm.dhgtjg }">
|
||||
{{ getDhgtjgLabel(serviceForm.dhgtjg) || "请选择" }}
|
||||
<span class="arrow-down">▼</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-item required" v-if="serviceForm.demandType == '4' || serviceForm.demandType == '5'">
|
||||
<label class="form-label">帮扶内容</label>
|
||||
<div class="form-value" @click="showPicker('serviceContent')" :class="{ noValue: !serviceForm.serviceContent }">
|
||||
{{ getServiceContentLabel(serviceForm.serviceContent) || "请选择" }}
|
||||
<span class="arrow-down">▼</span>
|
||||
</div>
|
||||
</div>
|
||||
</view>
|
||||
|
||||
<view class="inner-part">
|
||||
<div class="form-item required" v-if="serviceForm.demandType == '4' || serviceForm.demandType == '5'">
|
||||
<label class="form-label">帮扶情况说明</label>
|
||||
<textarea
|
||||
v-model="serviceForm.blqksm"
|
||||
style="width: 100%; border: none; padding: 10px 0; min-height: 100px; resize: none;"
|
||||
placeholder="请输入"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
|
||||
// </view>
|
||||
|
||||
<view class="inner-part" v-if="serviceForm.demandType == '4' || serviceForm.demandType == '5'">
|
||||
<div class="form-item">
|
||||
<label class="form-label">附件</label>
|
||||
<div style="width: 100%; padding: 10px 0;">
|
||||
<button @click="triggerFileUpload" style="padding: 8px 16px; background: #f0f0f0; border: none; border-radius: 4px;">
|
||||
上传附件 (最多6个)
|
||||
</button>
|
||||
<!-- 简单的文件列表显示 -->
|
||||
<div v-if="serviceForm.fileUrl.length > 0" class="file-list">
|
||||
<div v-for="(file, index) in serviceForm.fileUrl" :key="index" class="file-item">
|
||||
{{ file.name }}
|
||||
<span @click="removeFile(index)" style="margin-left: 10px; cursor: pointer; color: #ff4444;">删除</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</view>
|
||||
</div>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="button-area">
|
||||
<view class="btn" @click="cancelPage">取消</view>
|
||||
<view class="btn save" @click="submitServiceForm">保存</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 通用选择器 -->
|
||||
<div v-if="currentPicker" class="datetime-picker-overlay">
|
||||
<div class="datetime-picker">
|
||||
<div class="picker-header">
|
||||
<span @click="cancelPicker" style="padding: 10px;">取消</span>
|
||||
<span style="font-weight: bold;">{{ getPickerTitle(currentPicker) }}</span>
|
||||
<span @click="confirmPicker" style="padding: 10px; color: #007AFF;">确定</span>
|
||||
</div>
|
||||
<div class="picker-content">
|
||||
<div v-if="currentPicker === 'practicalSolutionTime'">
|
||||
<input
|
||||
type="datetime-local"
|
||||
v-model="manualDateTime"
|
||||
style="width: 100%; padding: 15px; box-sizing: border-box;"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="picker-options">
|
||||
<div
|
||||
v-for="option in getPickerOptions(currentPicker)"
|
||||
:key="option.value"
|
||||
@click="selectPickerOption(option.value)"
|
||||
:class="{ 'selected': selectedOption === option.value }"
|
||||
>
|
||||
{{ option.label }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getJbrInfo} from "../../api/personinfo/index"
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
serviceForm: {
|
||||
serviceObjectName: '',
|
||||
userId: '',
|
||||
demandType: '', // 帮扶方式
|
||||
practicalSolutionTime: '', // 帮扶时间
|
||||
agentUserId: '', // 经办人ID
|
||||
agentUserName: '', // 经办人名称
|
||||
dhgtjg: '', // 电话沟通结果
|
||||
serviceContent: '', // 帮扶内容
|
||||
blqksm: '', // 帮扶情况说明
|
||||
personStatus: '', // 人员状态
|
||||
fileUrl: [] // 附件
|
||||
},
|
||||
jingbrList1:[],
|
||||
currentPicker: null,
|
||||
selectedOption: '',
|
||||
manualDateTime: this.formatDateTime(new Date()),
|
||||
// 帮扶方式选项 (4: 上门服务, 5: 电话回访)
|
||||
demandTypeOptions: [
|
||||
{ value: '4', label: '上门服务' },
|
||||
{ value: '5', label: '电话回访' }
|
||||
],
|
||||
// 经办人选项(模拟数据)
|
||||
jingbrList: [
|
||||
{ userId: '1', nickName: '张三' },
|
||||
{ userId: '2', nickName: '李四' },
|
||||
{ userId: '3', nickName: '王五' }
|
||||
],
|
||||
// 电话沟通结果选项
|
||||
dhgtjgOptions: [
|
||||
{ value: '1', label: '已沟通' },
|
||||
{ value: '2', label: '未接通' },
|
||||
{ value: '3', label: '拒绝沟通' }
|
||||
],
|
||||
// 帮扶内容选项
|
||||
serviceContentOptions: [
|
||||
{ value: '1', label: '政策宣传' },
|
||||
{ value: '2', label: '就业指导' },
|
||||
{ value: '3', label: '技能培训' },
|
||||
{ value: '4', label: '岗位推荐' },
|
||||
{ value: '5', label: '其他' }
|
||||
],
|
||||
// 人员状态选项
|
||||
personStatusOptions: [
|
||||
{ value: '1', label: '已就业' },
|
||||
{ value: '2', label: '未就业' },
|
||||
{ value: '3', label: '灵活就业' }
|
||||
]
|
||||
};
|
||||
},
|
||||
async created(){
|
||||
this.getJbrInfo11()
|
||||
const serviceContentOptions = await this.$getDictSelectOption('qcjy_fwnr');
|
||||
console.log('帮扶内容选项:', serviceContentOptions);
|
||||
this.serviceContentOptions = serviceContentOptions;
|
||||
const serviceContentOptions1 = await this.$getDictSelectOption('qcjy_ryzt');
|
||||
this.personStatusOptions=serviceContentOptions1
|
||||
},
|
||||
onLoad(options) {
|
||||
if (options.name) {
|
||||
this.serviceForm.serviceObjectName = options.name;
|
||||
}
|
||||
if (options.id) {
|
||||
this.serviceForm.userId = options.id;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getJbrInfo11(){
|
||||
const res=await getJbrInfo()
|
||||
this.jingbrList1=res
|
||||
},
|
||||
// 显示选择器
|
||||
showPicker(type) {
|
||||
this.currentPicker = type;
|
||||
if (type !== 'practicalSolutionTime') {
|
||||
this.selectedOption = this.serviceForm[type] || '';
|
||||
}
|
||||
},
|
||||
// 取消选择器
|
||||
cancelPicker() {
|
||||
this.currentPicker = null;
|
||||
},
|
||||
// 确认选择器
|
||||
confirmPicker() {
|
||||
if (this.currentPicker === 'practicalSolutionTime') {
|
||||
this.manualConfirmDate();
|
||||
} else {
|
||||
this.serviceForm[this.currentPicker] = this.selectedOption;
|
||||
// 特殊处理经办人,同时保存名称
|
||||
if (this.currentPicker === 'agentUserId') {
|
||||
const agent = this.jingbrList1.find(item => item.userId === this.selectedOption);
|
||||
this.serviceForm.agentUserName = agent ? agent.nickName : '';
|
||||
}
|
||||
}
|
||||
this.currentPicker = null;
|
||||
},
|
||||
// 选择选项
|
||||
selectPickerOption(value) {
|
||||
this.selectedOption = value;
|
||||
},
|
||||
// 获取选择器标题
|
||||
getPickerTitle(type) {
|
||||
const titles = {
|
||||
demandType: '选择帮扶方式',
|
||||
practicalSolutionTime: '选择帮扶时间',
|
||||
agentUserId: '选择经办人',
|
||||
dhgtjg: '选择电话沟通结果',
|
||||
serviceContent: '选择帮扶内容'
|
||||
};
|
||||
return titles[type] || '请选择';
|
||||
},
|
||||
// 获取选择器选项
|
||||
getPickerOptions(type) {
|
||||
const options = {
|
||||
demandType: this.demandTypeOptions || [],
|
||||
agentUserId: this.jingbrList1 && this.jingbrList1.length > 0 ?
|
||||
this.jingbrList1.map(item => ({ value: item.userId, label: item.nickName })) : [],
|
||||
dhgtjg: this.dhgtjgOptions || [],
|
||||
serviceContent: this.serviceContentOptions || []
|
||||
};
|
||||
return options[type] || [];
|
||||
},
|
||||
// 获取帮扶方式标签
|
||||
getDemandTypeLabel(value) {
|
||||
const option = this.demandTypeOptions.find(item => item.value === value);
|
||||
return option ? option.label : '';
|
||||
},
|
||||
// 获取经办人名称
|
||||
getAgentUserName(userId) {
|
||||
const agent = this.jingbrList1.find(item => item.userId === userId);
|
||||
return agent ? agent.nickName : '';
|
||||
},
|
||||
// 处理经办人选择变化
|
||||
handleAgentChange(value) {
|
||||
if (!value || !this.jingbrList1 || !this.jingbrList1.length) {
|
||||
this.serviceForm.agentUserName = '';
|
||||
return;
|
||||
}
|
||||
const user = this.jingbrList1.find(item => item.userId === value);
|
||||
if (user) {
|
||||
this.serviceForm.agentUserName = user.nickName;
|
||||
} else {
|
||||
this.serviceForm.agentUserName = '';
|
||||
}
|
||||
},
|
||||
// 获取电话沟通结果标签
|
||||
getDhgtjgLabel(value) {
|
||||
const option = this.dhgtjgOptions.find(item => item.value === value);
|
||||
return option ? option.label : '';
|
||||
},
|
||||
// 获取帮扶内容标签
|
||||
getServiceContentLabel(value) {
|
||||
const option = this.serviceContentOptions.find(item => item.value === value);
|
||||
return option ? option.label : '';
|
||||
},
|
||||
// 格式化日期时间为YYYY-MM-DDTHH:MM格式(datetime-local输入框需要)
|
||||
formatDateTime(date) {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}T${hours}:${minutes}`;
|
||||
},
|
||||
|
||||
// 手动确认日期选择
|
||||
manualConfirmDate() {
|
||||
// 将datetime-local格式转换为显示格式
|
||||
const date = new Date(this.manualDateTime);
|
||||
this.serviceForm.practicalSolutionTime = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;
|
||||
},
|
||||
|
||||
// 触发文件上传
|
||||
triggerFileUpload() {
|
||||
if (this.serviceForm.fileUrl.length < 6) {
|
||||
// 模拟添加文件
|
||||
const mockFiles = ['文档.pdf', '图片.jpg', '表格.xlsx', '报告.docx'];
|
||||
const randomFile = mockFiles[Math.floor(Math.random() * mockFiles.length)];
|
||||
this.serviceForm.fileUrl.push({
|
||||
name: randomFile,
|
||||
url: `mock-url/${Date.now()}/${randomFile}`
|
||||
});
|
||||
} else {
|
||||
uni.showToast({ title: '最多只能上传6个文件', icon: 'none' });
|
||||
}
|
||||
},
|
||||
|
||||
// 删除文件
|
||||
removeFile(index) {
|
||||
this.serviceForm.fileUrl.splice(index, 1);
|
||||
},
|
||||
|
||||
// 取消页面
|
||||
cancelPage() {
|
||||
uni.navigateBack();
|
||||
},
|
||||
|
||||
// 验证表单
|
||||
validateForm() {
|
||||
if (!this.serviceForm.serviceObjectName) {
|
||||
uni.showToast({ title: '请填写被帮扶对象', icon: 'none' });
|
||||
return false;
|
||||
}
|
||||
if (!this.serviceForm.demandType) {
|
||||
uni.showToast({ title: '请选择帮扶方式', icon: 'none' });
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果是上门服务或电话回访,需要验证更多字段
|
||||
if (this.serviceForm.demandType === '4' || this.serviceForm.demandType === '5') {
|
||||
if (!this.serviceForm.practicalSolutionTime) {
|
||||
uni.showToast({ title: '请选择帮扶时间', icon: 'none' });
|
||||
return false;
|
||||
}
|
||||
if (!this.serviceForm.agentUserId) {
|
||||
uni.showToast({ title: '请选择经办人', icon: 'none' });
|
||||
return false;
|
||||
}
|
||||
if (this.serviceForm.demandType === '5' && !this.serviceForm.dhgtjg) {
|
||||
uni.showToast({ title: '请选择电话沟通结果', icon: 'none' });
|
||||
return false;
|
||||
}
|
||||
if (!this.serviceForm.serviceContent) {
|
||||
uni.showToast({ title: '请选择帮扶内容', icon: 'none' });
|
||||
return false;
|
||||
}
|
||||
if (!this.serviceForm.blqksm) {
|
||||
uni.showToast({ title: '请填写帮扶情况说明', icon: 'none' });
|
||||
return false;
|
||||
}
|
||||
if (!this.serviceForm.personStatus) {
|
||||
uni.showToast({ title: '请选择人员状态', icon: 'none' });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
// 提交表单
|
||||
async submitServiceForm() {
|
||||
try {
|
||||
// 验证表单
|
||||
if (!this.validateForm()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 模拟加载状态
|
||||
uni.showLoading({ title: '保存中...' });
|
||||
|
||||
// 准备提交数据
|
||||
const submitData = {
|
||||
...this.serviceForm,
|
||||
// 格式化文件数据
|
||||
fileUrl: JSON.stringify(this.serviceForm.fileUrl)
|
||||
};
|
||||
|
||||
// 打印提交数据
|
||||
console.log('提交数据:', submitData);
|
||||
|
||||
// 模拟API调用延迟
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
uni.showToast({ title: '保存成功', icon: 'success' });
|
||||
uni.navigateBack();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
uni.showToast({ title: '保存失败', icon: 'none' });
|
||||
} finally {
|
||||
uni.hideLoading();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page {
|
||||
height: 100vh;
|
||||
background-color: #eef1f5 !important;
|
||||
background-image: url("~@/static/images/top.png");
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% auto;
|
||||
}
|
||||
|
||||
.input-outer-part {
|
||||
background: #eef1f5;
|
||||
padding: 32rpx;
|
||||
padding-top: 100rpx; /* 增加顶部内边距,防止内容被遮挡 */
|
||||
position: relative;
|
||||
top: -80rpx;
|
||||
border-radius: 32rpx 32rpx 0 0;
|
||||
}
|
||||
|
||||
.inner {
|
||||
.inner-part {
|
||||
background: #fff;
|
||||
padding: 0 32rpx;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.self-form {
|
||||
// 表单样式
|
||||
}
|
||||
|
||||
/* 表单项目样式 */
|
||||
.form-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
/* 选择器容器样式 */
|
||||
.form-select-wrapper {
|
||||
flex: 1;
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.form-item.required .form-label::after {
|
||||
content: '*';
|
||||
color: #ff4444;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
width: 110px;
|
||||
flex-shrink: 0;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.form-value {
|
||||
flex: 1;
|
||||
padding: 10px 0;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.form-value.noValue {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.arrow-down {
|
||||
font-size: 12px;
|
||||
color: #A6A6A6;
|
||||
}
|
||||
|
||||
.edit-icon {
|
||||
font-size: 14px;
|
||||
color: #A6A6A6;
|
||||
}
|
||||
|
||||
.form-input-wrapper {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form-input-wrapper input {
|
||||
width: 100%;
|
||||
padding: 10px 0;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-input-wrapper .edit-icon {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
/* 单选按钮样式 */
|
||||
.radio-group {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.radio-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 20px;
|
||||
margin-bottom: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.radio-item input[type="radio"] {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.radio-label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 文件列表样式 */
|
||||
.file-list {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 0;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 选择器样式 */
|
||||
.datetime-picker-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.datetime-picker {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
width: 80%;
|
||||
max-width: 400px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.picker-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.picker-content {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.picker-options {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.picker-options > div {
|
||||
padding: 15px 20px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.picker-options > div.selected {
|
||||
background-color: #f0f8ff;
|
||||
color: #1d64cf;
|
||||
}
|
||||
|
||||
.noValue {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
.button-area {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 24rpx 32rpx;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.btn {
|
||||
line-height: 72rpx;
|
||||
width: 176rpx;
|
||||
margin-right: 16rpx;
|
||||
font-size: 28rpx;
|
||||
border: 1px solid #b8c5d4;
|
||||
color: #282828;
|
||||
text-align: center;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.save {
|
||||
background: linear-gradient(103deg, #1d64cf 0%, #1590d4 99%);
|
||||
color: #fff;
|
||||
border: 0;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,10 +1,9 @@
|
||||
<!--
|
||||
* @Date: 2025-10-16 15:15:47
|
||||
* @LastEditors: shirlwang
|
||||
* @LastEditTime: 2025-10-31 14:56:32
|
||||
* @LastEditTime: 2025-10-22 14:17:46
|
||||
-->
|
||||
<template>
|
||||
<view>
|
||||
<!-- @scroll="handleScroll" @scrolltolower="scrollBottom" -->
|
||||
<scroll-view :scroll-y="true" class="container" style="background-image: url('../../../packageRc/static/pageBg.png');">
|
||||
<view style="padding: 40rpx 28rpx;">
|
||||
@@ -30,7 +29,7 @@
|
||||
<image src="../../../packageRc/static/trace.png"/>
|
||||
毕业生追踪
|
||||
</view>
|
||||
<view style="display: flex;justify-content: space-between;" @click="goPersonalList">
|
||||
<view style="display: flex;justify-content: space-between;">
|
||||
<view>点击查看</view>
|
||||
<uni-icons color="#639AEB" type="arrow-right" size="16"></uni-icons>
|
||||
</view>
|
||||
@@ -48,9 +47,9 @@
|
||||
</view>
|
||||
<view class="titles">
|
||||
<view class="title-item active"><view>待办需求预警列表</view></view>
|
||||
<view>共 {{jobList1count}}条信息</view>
|
||||
<view>共 2 条信息</view>
|
||||
</view>
|
||||
<view v-for="(item, index) in jobList1" :key="index" class="job-list">
|
||||
<view v-for="(item, index) in jobList" :key="index" class="job-list">
|
||||
<view class="title">销售顾问</view>
|
||||
<view class="info">
|
||||
待办内容文字示例待办内容文字示例待办内容文字示例待办内容文字示例
|
||||
@@ -62,7 +61,7 @@
|
||||
</view>
|
||||
<view class="titles">
|
||||
<view class="title-item active"><view>待服务毕业生列表</view></view>
|
||||
<view>共 {{jobListcount}} 条信息</view>
|
||||
<view>共 22 条信息</view>
|
||||
</view>
|
||||
<view v-for="(item, index) in jobList" :key="index" class="person-list">
|
||||
<view class="top-info">
|
||||
@@ -70,7 +69,7 @@
|
||||
<image v-else src="../../../packageRc/static/personIconFe.png"/>
|
||||
<view class="top-right">
|
||||
<view class="name-line">
|
||||
<view class="name">姓名<view class="tag">{{item.name}}</view></view>
|
||||
<view class="name">姓名<view class="tag">硕士</view></view>
|
||||
<view class="service-status">·未服务</view>
|
||||
</view>
|
||||
<view class="info-line" style="display: flex;">
|
||||
@@ -81,146 +80,26 @@
|
||||
</view>
|
||||
<view class="info-line">
|
||||
<view><text>联系电话:</text>152****5488</view>
|
||||
<view><text>详细地址:</text>{{item.xxdz}}</view>
|
||||
<view><text>详细地址:</text>山东省济南市历城区港沟街道融创文旅城鹊桥华居8号楼1单元801</view>
|
||||
</view>
|
||||
<view class="services">
|
||||
<view @click="showReturnReasonPopup">退回</view>
|
||||
<view @click="tiao(item.id)">服务</view>
|
||||
<view>退回</view>
|
||||
<view>服务</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 退回原因弹窗 -->
|
||||
<uni-popup ref="returnReasonPopup" position="center" round>
|
||||
<view class="popup-content" style="background:rgb(248, 248, 248);">
|
||||
|
||||
<textarea
|
||||
v-model="returnReason"
|
||||
class="reason-textarea"
|
||||
placeholder="请输入退回原因"
|
||||
placeholder-class="placeholder-style"
|
||||
rows="5"
|
||||
maxlength="200"
|
||||
></textarea>
|
||||
<view class="popup-footer">
|
||||
<button class="cancel-btn" @click="cancelReturn">取消</button>
|
||||
<button class="confirm-btn" @click="confirmReturn">确认退回</button>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { personInfoList,personAlertList } from '../../api/personinfo/index'
|
||||
import { reactive, inject, watch, ref, onMounted, watchEffect, nextTick } from 'vue';
|
||||
let activeTab = ref(1)
|
||||
let activeTitle = ref(1)
|
||||
let jobList = ref([{},{},{},{},{}])
|
||||
let jobListcount = ref()
|
||||
let jobList1 = ref([{},{}])
|
||||
let jobList1count = ref()
|
||||
// 退回原因弹窗相关
|
||||
let returnReasonPopup = ref(null)
|
||||
let returnReason = ref('')
|
||||
let currentItemIndex = ref(-1)
|
||||
function goPersonalList() {
|
||||
console.log('goPersonalList')
|
||||
uni.navigateTo({
|
||||
url: '/packageRc/pages/personalList/personalList'
|
||||
});
|
||||
}
|
||||
function back() {
|
||||
uni.navigateBack({
|
||||
delta: 1
|
||||
});
|
||||
|
||||
};
|
||||
onMounted(() => {
|
||||
getlist();
|
||||
getlistyujing();
|
||||
});
|
||||
|
||||
async function getlist(){
|
||||
|
||||
try {
|
||||
const res = await personInfoList();
|
||||
console.log("res", res);
|
||||
jobList.value = res.rows || [];
|
||||
jobListcount.value=res.total || 0
|
||||
} catch (error) {
|
||||
console.error("获取数据失败:", error);
|
||||
jobList.value = [];
|
||||
}
|
||||
};
|
||||
async function getlistyujing(){
|
||||
|
||||
try {
|
||||
const res = await personAlertList();
|
||||
console.log("res", res);
|
||||
jobList1.value = res.rows || [];
|
||||
jobList1count.value=res.total || 0
|
||||
} catch (error) {
|
||||
console.error("获取数据失败:", error);
|
||||
jobList1.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
// 显示退回原因弹窗
|
||||
function showReturnReasonPopup() {
|
||||
console.log("退回")
|
||||
returnReason.value = ''
|
||||
// 使用 ref 控制弹窗显示
|
||||
if (returnReasonPopup.value) {
|
||||
returnReasonPopup.value.open()
|
||||
}
|
||||
}
|
||||
|
||||
function tiao(id){
|
||||
console.log('尝试导航到待办详情页面,ID:', id);
|
||||
// 尝试直接使用uni.navigateTo,使用正确的格式并传递id参数
|
||||
uni.navigateTo({
|
||||
url: `/packageRc/pages/daiban/daibandetail?id=${id}`,
|
||||
success: function() {
|
||||
console.log('导航成功');
|
||||
},
|
||||
fail: function(err) {
|
||||
console.error('导航失败:', err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 确认退回
|
||||
function confirmReturn() {
|
||||
if (!returnReason.value.trim()) {
|
||||
uni.showToast({
|
||||
title: '请填写退回原因',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 这里可以添加提交退回原因的API调用
|
||||
console.log('退回原因:', returnReason.value, '项目索引:', currentItemIndex.value)
|
||||
|
||||
// 模拟提交成功
|
||||
uni.showToast({
|
||||
title: '退回成功'
|
||||
})
|
||||
|
||||
// 使用 ref 控制弹窗关闭
|
||||
if (returnReasonPopup.value) {
|
||||
returnReasonPopup.value.close()
|
||||
}
|
||||
}
|
||||
|
||||
// 取消退回
|
||||
function cancelReturn() {
|
||||
// 使用 ref 控制弹窗关闭
|
||||
if (returnReasonPopup.value) {
|
||||
returnReasonPopup.value.close()
|
||||
}
|
||||
}
|
||||
function viewMore() {
|
||||
// uni.navigateTo({
|
||||
@@ -260,7 +139,6 @@ view{box-sizing: border-box;display: block;}
|
||||
margin-top: 16rpx;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
// text{
|
||||
// font-size: 28rpx;
|
||||
// }
|
||||
@@ -270,75 +148,6 @@ view{box-sizing: border-box;display: block;}
|
||||
// margin-bottom: 15rpx;
|
||||
height: 78rpx;
|
||||
}
|
||||
|
||||
/* 退回原因弹窗样式 */
|
||||
.popup-content {
|
||||
background: #FFFF;
|
||||
border-radius: 24rpx;
|
||||
padding: 48rpx;
|
||||
width: 88%;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.popup-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #282828;
|
||||
margin-bottom: 36rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.reason-textarea {
|
||||
width: 100%;
|
||||
border: 2rpx solid #D8D8D8;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
min-height: 220rpx;
|
||||
font-size: 30rpx;
|
||||
color: #333333;
|
||||
box-sizing: border-box;
|
||||
background: #FAFAFA;
|
||||
}
|
||||
|
||||
.placeholder-style {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.popup-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 40rpx;
|
||||
}
|
||||
|
||||
.cancel-btn,
|
||||
.confirm-btn {
|
||||
flex: 1;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
border-radius: 16rpx;
|
||||
font-size: 32rpx;
|
||||
text-align: center;
|
||||
margin: 0 15rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
background: #F8F8F8;
|
||||
color: #666666;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
background: #1A62CE;
|
||||
color: #FFFFFF;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* 弹窗背景遮罩层样式 */
|
||||
::v-deep(.uni-popup__wrapper) {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
.trace-line{
|
||||
width: 100%;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user