培训机构评价机构

This commit is contained in:
2026-02-24 10:06:48 +08:00
parent a8a9d792a3
commit 8a91bef819
12 changed files with 5181 additions and 0 deletions

View File

@@ -16,6 +16,7 @@ import Empty from './components/empty/empty.vue';
import NoBouncePage from '@/components/NoBouncePage/NoBouncePage.vue'
import MsgTips from '@/components/MsgTips/MsgTips.vue'
import SelectPopupPlugin from '@/components/selectPopup/selectPopupPlugin';
import unieditor from '@/packageB/components/unieditor.vue'
import storeRc from './utilsRc/store/index.js'
import {processFileUrl,} from '@/utilsRc/common.js'
// iconfont.css 已在 App.vue 中通过 @import 引入,无需在此处重复引入
@@ -44,6 +45,7 @@ export function createApp() {
app.component('Empty', Empty)
app.component('NoBouncePage', NoBouncePage)
app.component('MsgTips', MsgTips)
app.component('unieditor', unieditor)
app.config.globalProperties.$processFileUrl = processFileUrl;
@@ -74,6 +76,7 @@ export function createApp() {
app.provide('deviceInfo', globalFunction.getdeviceInfo());
app.use(SelectPopupPlugin);
// app.use(unieditor);
app.use(Pinia.createPinia());
// 注册vuex
app.use(storeRc);

6
package-lock.json generated
View File

@@ -7,6 +7,7 @@
"dependencies": {
"@dcloudio/uni-ui": "^1.5.11",
"dayjs": "^1.11.19",
"mp-html": "^2.5.2",
"sm-crypto": "^0.3.13"
}
},
@@ -25,6 +26,11 @@
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
},
"node_modules/mp-html": {
"version": "2.5.2",
"resolved": "https://registry.npmmirror.com/mp-html/-/mp-html-2.5.2.tgz",
"integrity": "sha512-45e8c32Qgux4YU4iC3qCSFsOh3y+RwPwZ+iz/vvLkDgSGWk+1zsL4WUzWWQc9w3AsAfkaD/QR0oIufIDngBmXA=="
},
"node_modules/sm-crypto": {
"version": "0.3.13",
"resolved": "https://registry.npmmirror.com/sm-crypto/-/sm-crypto-0.3.13.tgz",

View File

@@ -2,6 +2,7 @@
"dependencies": {
"@dcloudio/uni-ui": "^1.5.11",
"dayjs": "^1.11.19",
"mp-html": "^2.5.2",
"sm-crypto": "^0.3.13"
}
}

827
packageB/components/map.vue Normal file
View File

@@ -0,0 +1,827 @@
<template>
<AppLayout title="选择地址" :showBack="true">
<view class="map-container">
<!-- 搜索框 -->
<!-- <view class="search-box">
<view class="search-input-wrapper">
<uni-icons type="search" size="20" color="#999"></uni-icons>
<input
class="search-input"
v-model="searchKeyword"
placeholder="输入关键词搜索地址(支持模糊搜索)"
@input="onSearchInput"
@confirm="searchLocation"
/>
<uni-icons
v-if="searchKeyword"
type="clear"
size="18"
color="#999"
@click="clearSearch"
></uni-icons>
</view>
</view> -->
<!-- 搜索结果列表 -->
<!-- <view class="search-results" v-if="showSearchResults">
<scroll-view scroll-y class="results-scroll" v-if="searchResults.length > 0">
<view
class="result-item"
v-for="(item, index) in searchResults"
:key="index"
@click="selectSearchResult(item)"
>
<view class="result-name">{{ item.name }}</view>
<view class="result-address">{{ item.address }}</view>
</view>
</scroll-view>
<view class="empty-results" v-else-if="isSearching">
<view class="loading-icon">
<uni-icons type="loop" size="40" color="#999"></uni-icons>
</view>
<text>搜索中...</text>
</view>
<view class="empty-results" v-else>
<uni-icons type="info" size="40" color="#999"></uni-icons>
<text>未找到相关地址请尝试其他关键词</text>
<view class="search-tips">
<text class="tip-title">搜索建议</text>
<text class="tip-item"> 输入具体地址名称</text>
<text class="tip-item"> 输入地标建筑名称</text>
<text class="tip-item"> 输入街道或区域名称</text>
</view>
</view>
</view> -->
<!-- 地图 -->
<view class="map-wrapper" v-show="!showSearchResults">
<!-- #ifdef H5 -->
<view id="amap-container" class="amap-container"></view>
<!-- #endif -->
<!-- #ifndef H5 -->
<map
id="map"
class="map"
:latitude="latitude"
:longitude="longitude"
:markers="markers"
:show-location="true"
@regionchange="onRegionChange"
@tap="onMapTap"
>
<!-- <cover-view class="map-center-marker">
<cover-image src="/static/icon/Location.png" class="marker-icon"></cover-image>
</cover-view> -->
</map>
<!-- #endif -->
</view>
<!-- 当前位置信息 -->
<view class="location-info" v-if="currentAddress && !showSearchResults">
<view class="info-title">当前选择位置</view>
<view class="info-name">{{ currentAddress.name }}</view>
<view class="info-address">{{ currentAddress.address }}</view>
</view>
<!-- 底部按钮 -->
<view class="bottom-actions">
<!-- <button class="locate-btn" @click="getCurrentLocation" :disabled="isLocating">
<uni-icons type="location-filled" size="20" color="#256BFA"></uni-icons>
<text>{{ isLocating ? '定位中...' : '定位' }}</text>
</button> -->
<button class="confirm-btn" @click="confirmLocation">确认选择</button>
</view>
</view>
</AppLayout>
</template>
<script setup>
import { ref, inject, onMounted } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
const { $api } = inject('globalFunction')
// 搜索相关
const searchKeyword = ref('')
const searchResults = ref([])
const showSearchResults = ref(false)
const isSearching = ref(false)
const isLocating = ref(false)
let searchTimer = null
// 地图相关
const latitude = ref(39.473651)
const longitude = ref(76.000311)
const markers = ref([])
const currentAddress = ref(null)
// H5地图实例
let map = null
let AMap = null
let geocoder = null
let placeSearch = null
onLoad((options) => {
// 可以接收初始位置参数
if (options.location) {
let obj=options.location.split(',')
latitude.value = parseFloat(obj[1])
longitude.value = parseFloat(obj[0])
}
})
onMounted(() => {
// #ifdef H5
initAmapH5()
// #endif
// #ifndef H5
// 先设置默认位置,避免地图显示空白
markers.value = [{
id: 1,
latitude: latitude.value,
longitude: longitude.value,
// iconPath: '/static/icon/Location.png',
width: 25,
height: 30
}]
// 延迟执行定位,避免页面加载时立即定位失败
// setTimeout(() => {
// getCurrentLocation()
// }, 1000)
// #endif
})
// H5端初始化高德地图
const initAmapH5 = () => {
// #ifdef H5
if (window.AMap) {
AMap = window.AMap
initMap()
} else {
const script = document.createElement('script')
script.src = 'https://webapi.amap.com/maps?v=2.0&key=9cfc9370bd8a941951da1cea0308e9e3&plugin=AMap.Geocoder,AMap.PlaceSearch'
script.onload = () => {
AMap = window.AMap
initMap()
}
document.head.appendChild(script)
}
// #endif
}
// 初始化地图
const initMap = () => {
// #ifdef H5
map = new AMap.Map('amap-container', {
zoom: 13,
center: [longitude.value, latitude.value],
resizeEnable: true
})
// 创建标记
const marker = new AMap.Marker({
position: [longitude.value, latitude.value],
draggable: true
})
marker.on('dragend', (e) => {
const position = e.target.getPosition()
longitude.value = position.lng
latitude.value = position.lat
reverseGeocode(position.lng, position.lat)
})
map.add(marker)
// 初始化地理编码
geocoder = new AMap.Geocoder({
city: '全国'
})
// 初始化地点搜索
placeSearch = new AMap.PlaceSearch({
city: '全国',
pageSize: 10
})
// 地图点击事件
map.on('click', (e) => {
debugger
const { lng, lat } = e.lnglat
longitude.value = lng
latitude.value = lat
marker.setPosition([lngyy, lat])
// reverseGeocode(lng, lat)
})
// 获取当前位置信息
reverseGeocode(longitude.value, latitude.value)
// #endif
}
// 搜索输入
const onSearchInput = () => {
if (searchTimer) {
clearTimeout(searchTimer)
}
if (!searchKeyword.value.trim()) {
showSearchResults.value = false
searchResults.value = []
isSearching.value = false
return
}
showSearchResults.value = true
isSearching.value = true
searchTimer = setTimeout(() => {
if (searchKeyword.value.trim()) {
searchLocation()
}
}, 300) // 优化防抖时间从500ms改为300ms
}
// 搜索地点
const searchLocation = () => {
if (!searchKeyword.value.trim()) {
return
}
showSearchResults.value = true
isSearching.value = true
// #ifdef H5
if (placeSearch) {
placeSearch.search(searchKeyword.value, (status, result) => {
isSearching.value = false
if (status === 'complete' && result.poiList) {
searchResults.value = result.poiList.pois.map(poi => ({
name: poi.name,
address: poi.address || poi.pname + poi.cityname + poi.adname,
location: poi.location,
lng: poi.location.lng,
lat: poi.location.lat
}))
} else {
searchResults.value = []
}
})
}
// #endif
// #ifndef H5
// 小程序端使用uni.request调用高德API
uni.request({
url: 'https://restapi.amap.com/v3/place/text',
data: {
key: '9cfc9370bd8a941951da1cea0308e9e3',
keywords: searchKeyword.value,
city: '全国',
offset: 20,
citylimit: false, // 不限制城市,支持全国搜索
extensions: 'all' // 返回详细信息
},
success: (res) => {
isSearching.value = false
console.log('搜索响应:', res.data) // 调试日志
if (res.data.status === '1' && res.data.pois && res.data.pois.length > 0) {
searchResults.value = res.data.pois.map(poi => {
const [lng, lat] = poi.location.split(',')
return {
name: poi.name,
address: poi.address || `${poi.pname || ''}${poi.cityname || ''}${poi.adname || ''}`,
lng: parseFloat(lng),
lat: parseFloat(lat)
}
})
console.log('搜索结果:', searchResults.value) // 调试日志
} else {
// 如果第一次搜索没有结果,尝试更宽泛的搜索
if (searchKeyword.value.length > 2) {
tryAlternativeSearch()
} else {
searchResults.value = []
console.log('搜索无结果:', res.data) // 调试日志
}
}
},
fail: (err) => {
isSearching.value = false
searchResults.value = []
console.error('搜索请求失败:', err) // 调试日志
$api.msg('搜索失败,请检查网络连接')
}
})
// #endif
}
// 备用搜索策略
const tryAlternativeSearch = () => {
// 尝试使用地理编码API搜索
uni.request({
url: 'https://restapi.amap.com/v3/geocode/geo',
data: {
key: '9cfc9370bd8a941951da1cea0308e9e3',
address: searchKeyword.value,
city: '全国'
},
success: (res) => {
isSearching.value = false
console.log('备用搜索响应:', res.data) // 调试日志
if (res.data.status === '1' && res.data.geocodes && res.data.geocodes.length > 0) {
searchResults.value = res.data.geocodes.map(geo => {
const [lng, lat] = geo.location.split(',')
return {
name: geo.formatted_address,
address: geo.formatted_address,
lng: parseFloat(lng),
lat: parseFloat(lat)
}
})
console.log('备用搜索结果:', searchResults.value) // 调试日志
} else {
searchResults.value = []
console.log('备用搜索也无结果:', res.data) // 调试日志
}
},
fail: (err) => {
isSearching.value = false
searchResults.value = []
console.error('备用搜索失败:', err) // 调试日志
}
})
}
// 选择搜索结果
const selectSearchResult = (item) => {
longitude.value = item.lng
latitude.value = item.lat
currentAddress.value = {
name: item.name,
address: item.address,
longitude: item.lng,
latitude: item.lat
}
// #ifdef H5
if (map) {
map.setCenter([item.lng, item.lat])
const marker = map.getAllOverlays('marker')[0]
if (marker) {
marker.setPosition([item.lng, item.lat])
}
}
// #endif
// #ifndef H5
markers.value = [{
id: 1,
latitude: item.lat,
longitude: item.lng,
// iconPath: '/static/icon/Location.png',
width: 25,
height: 30
}]
// #endif
showSearchResults.value = false
searchKeyword.value = ''
}
// 清除搜索
const clearSearch = () => {
searchKeyword.value = ''
searchResults.value = []
showSearchResults.value = false
isSearching.value = false
if (searchTimer) {
clearTimeout(searchTimer)
}
}
// 逆地理编码(根据坐标获取地址)
const reverseGeocode = (lng, lat) => {
// #ifdef H5
if (geocoder) {
geocoder.getAddress([lng, lat], (status, result) => {
if (status === 'complete' && result.regeocode) {
const addressComponent = result.regeocode.addressComponent
const formattedAddress = result.regeocode.formattedAddress
currentAddress.value = {
name: addressComponent.building || addressComponent.township,
address: formattedAddress,
longitude: lng,
latitude: lat
}
}
})
}
// #endif
// #ifndef H5
uni.request({
url: 'https://restapi.amap.com/v3/geocode/regeo',
data: {
key: '9cfc9370bd8a941951da1cea0308e9e3',
location: `${lng},${lat}`
},
success: (res) => {
if (res.data.status === '1' && res.data.regeocode) {
const addressComponent = res.data.regeocode.addressComponent
const formattedAddress = res.data.regeocode.formatted_address
currentAddress.value = {
name: addressComponent.building || addressComponent.township || '选择的位置',
address: formattedAddress,
longitude: lng,
latitude: lat
}
}
}
})
// #endif
}
// 获取当前定位
const getCurrentLocation = () => {
if (isLocating.value) return // 防止重复定位
isLocating.value = true
uni.showLoading({ title: '定位中...' })
// 先检查定位权限
uni.getSetting({
success: (settingRes) => {
if (settingRes.authSetting['scope.userLocation'] === false) {
// 用户拒绝了定位权限,引导用户开启
isLocating.value = false
uni.hideLoading()
uni.showModal({
title: '定位权限',
content: '需要获取您的位置信息来提供更好的服务,请在设置中开启定位权限',
confirmText: '去设置',
success: (modalRes) => {
if (modalRes.confirm) {
uni.openSetting()
}
}
})
return
}
// 执行定位
uni.getLocation({
type: 'gcj02',
altitude: false,
success: (res) => {
console.log('定位成功:', res) // 调试日志
longitude.value = res.longitude
latitude.value = res.latitude
// #ifdef H5
if (map) {
map.setCenter([res.longitude, res.latitude])
const marker = map.getAllOverlays('marker')[0]
if (marker) {
marker.setPosition([res.longitude, res.latitude])
}
}
// #endif
// #ifndef H5
// 更新小程序端标记
markers.value = [{
id: 1,
latitude: res.latitude,
longitude: res.longitude,
// iconPath: '/static/icon/Location.png',
width: 25,
height: 30
}]
// #endif
reverseGeocode(res.longitude, res.latitude)
uni.hideLoading()
isLocating.value = false
},
fail: (err) => {
console.error('定位失败:', err) // 调试日志
uni.hideLoading()
isLocating.value = false
// 根据错误类型给出不同提示
// let errorMsg = '定位失败'
// if (err.errMsg.includes('auth deny')) {
// errorMsg = '定位权限被拒绝,请在设置中开启'
// } else if (err.errMsg.includes('timeout')) {
// errorMsg = '定位超时,请重试'
// } else if (err.errMsg.includes('network')) {
// errorMsg = '网络异常,请检查网络连接'
// }
// uni.showModal({
// title: '定位失败',
// content: errorMsg + ',是否使用默认位置?',
// confirmText: '使用默认位置',
// cancelText: '重试',
// success: (modalRes) => {
// if (modalRes.confirm) {
// // 使用默认位置(北京)
// longitude.value = 116.397428
// latitude.value = 39.90923
// reverseGeocode(longitude.value, latitude.value)
// } else {
// // 重试定位
// setTimeout(() => {
// getCurrentLocation()
// }, 2000)
// }
// }
// })
}
})
},
fail: () => {
uni.hideLoading()
isLocating.value = false
$api.msg('无法获取定位权限设置')
}
})
}
// 地图区域变化(小程序端)
const onRegionChange = (e) => {
// #ifndef H5
// 只有在用户手动拖动地图结束时才更新位置
if (e.type === 'end' && e.causedBy === 'drag') {
const mapContext = uni.createMapContext('map')
mapContext.getCenterLocation({
success: (res) => {
// debugger
longitude.value = res.longitude
latitude.value = res.latitude
markers.value = [{
id: 1,
latitude: latitude.value,
longitude: longitude.value,
width: 25,
height: 30
}]
// reverseGeocode(res.longitude, res.latitude)
}
})
}
// #endif
}
// 地图点击事件(小程序端)
const onMapTap = (e) => {
// #ifndef H5
const { latitude: lat, longitude: lng } = e.detail
longitude.value = lng
latitude.value = lat
// 更新标记
markers.value = [{
id: 1,
latitude: lat,
longitude: lng,
// iconPath: '/static/icon/Location.png',
width: 25,
height: 30
}]
reverseGeocode(lng, lat)
// #endif
}
// 确认选择
const confirmLocation = () => {
if (!longitude.value && !latitude.value) {
$api.msg('请选择地址')
return
}
if (typeof uni.globalParams === 'object') {
uni.globalParams = {
longitude: longitude.value,
latitude: latitude.value
}; // 示例使用全局变量实际应用中可以根据需要选择使用Vuex等
} else {
uni.globalParams = {}; // 初始化全局变量
uni.globalParams = {
longitude: longitude.value,
latitude: latitude.value
}; // 设置参数
}
uni.navigateBack({
delta: 1 // 返回上一页
});
}
</script>
<style lang="stylus" scoped>
.map-container
width: 100%
height: 100vh
position: relative
display: flex
flex-direction: column
.search-box
position: absolute
top: 20rpx
left: 32rpx
right: 32rpx
z-index: 10
.search-input-wrapper
background: #fff
border-radius: 40rpx
padding: 20rpx 30rpx
display: flex
align-items: center
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1)
.search-input
flex: 1
margin: 0 20rpx
font-size: 28rpx
uni-icons
flex-shrink: 0
.search-results
position: absolute
top: 100rpx
left: 32rpx
right: 32rpx
bottom: 180rpx
background: #fff
border-radius: 20rpx
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1)
z-index: 9
overflow: hidden
.results-scroll
height: 100%
.result-item
padding: 30rpx
border-bottom: 1rpx solid #f0f0f0
&:active
background: #f5f5f5
.result-name
font-size: 32rpx
color: #333
font-weight: 500
margin-bottom: 10rpx
.result-address
font-size: 26rpx
color: #999
.empty-results
height: 100%
display: flex
flex-direction: column
align-items: center
justify-content: center
color: #999
font-size: 28rpx
.loading-icon
animation: rotate 1s linear infinite
margin-bottom: 20rpx
uni-icons
margin-bottom: 20rpx
text
padding: 0 60rpx
text-align: center
line-height: 1.5
.search-tips
margin-top: 40rpx
padding: 0 40rpx
.tip-title
font-size: 26rpx
color: #666
font-weight: 500
margin-bottom: 20rpx
display: block
.tip-item
font-size: 24rpx
color: #999
line-height: 1.8
display: block
margin-bottom: 8rpx
@keyframes rotate
from
transform: rotate(0deg)
to
transform: rotate(360deg)
.map-wrapper
flex: 1
position: relative
.map, .amap-container
width: 100%
height: 80%
.map-center-marker
position: absolute
left: 50%
top: 50%
transform: translate(-50%, -100%)
z-index: 5
.marker-icon
width: 60rpx
height: 80rpx
.location-info
position: absolute
bottom: 180rpx
left: 32rpx
right: 32rpx
background: #fff
border-radius: 20rpx
padding: 30rpx
box-shadow: 0 -4rpx 12rpx rgba(0,0,0,0.1)
z-index: 10
.info-title
font-size: 24rpx
color: #999
margin-bottom: 10rpx
.info-name
font-size: 32rpx
color: #333
font-weight: 500
margin-bottom: 10rpx
.info-address
font-size: 28rpx
color: #666
.bottom-actions
position: absolute
bottom: 120rpx
left: 0
right: 0
background: #fff
padding: 20rpx 32rpx
display: flex
gap: 20rpx
box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.1)
z-index: 11
.locate-btn
width: 120rpx
height: 80rpx
background: #fff
border: 2rpx solid #256BFA
border-radius: 40rpx
display: flex
flex-direction: column
align-items: center
justify-content: center
font-size: 24rpx
color: #256BFA
&:disabled
opacity: 0.5
color: #999
border-color: #999
text
margin-top: 4rpx
.confirm-btn
flex: 1
height: 80rpx
background: #256BFA
color: #fff
border-radius: 40rpx
font-size: 32rpx
border: none
button::after
border: none
</style>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,941 @@
<template>
<div style="height: 90vh;overflow-y: auto;">
<view class="content">
<view class="content-input">
<view class="input-titile"><text class="input-required">*</text>机构名称</view>
<input class="input-con" v-model="fromValue.organName" placeholder="请输入机构名称" />
</view>
<view class="content-input">
<view class="input-titile">评价等级</view>
<view class="input-con">
<picker @change="bindPickerChange" :value="gradeIndex" :range="gradeLabels" range-key="dictLabel">
<view class="uni-input">{{gradeIndex==''?'请选择':gradeLabels[gradeIndex].dictLabel}}</view>
</picker>
</view>
</view>
<view class="content-input">
<view class="input-titile"><text class="input-required">*</text>联系人</view>
<input class="input-con" v-model="fromValue.contactName" placeholder="请输入联系人" />
</view>
<view class="content-input">
<view class="input-titile"><text class="input-required">*</text>联系方式</view>
<input class="input-con" v-model="fromValue.contactPhone" placeholder="请输入您的联系方式" />
</view>
<view class="content-input">
<view class="input-titile">工作时间</view>
<input class="input-con" v-model="fromValue.organWorkStarttime" placeholder="请输入" />
</view>
<view class="content-input">
<view class="input-titile">所在区划</view>
<view class="input-con">
<picker mode = 'multiSelector'
@change="onWorkTypePickerChange"
@columnchange="onWorkTypeColumnChange"
range-key="dictLabel"
:value="fromValue.organAddressArr"
:range="region.provinceData">
<view class="uni-input" v-if="isShow" style="width: 100%;">{{region.provinceData[0][region.arrayIndex[0]].dictLabel}}/{{region.provinceData[1][region.arrayIndex[1]].dictLabel}}/{{region.provinceData[2].length>0?region.provinceData[2][region.arrayIndex[2]].dictLabel:''}}</view>
<view class="uni-input" style="width: 100%;" v-else>请选择</view>
</picker>
</view>
<!-- <input class="input-con" v-model="fromValue.organAddressArr" placeholder="请选择" /> -->
</view>
<view class="content-input">
<view class="input-titile"><text class="input-required">*</text>机构地址</view>
<input class="input-con" v-model="fromValue.address" placeholder="请输入机构地址" />
</view>
<view class="content-input">
<view class="input-titile">地图定位(经纬度)</view>
<view @click="selectLocation" class="input-con">{{fromValue.location?fromValue.location:'请选择'}}</view>
</view>
<view class="content-input">
<view class="input-titile">评价项目</view>
<textarea auto-height class="input-con" v-model="fromValue.evaluationItems" placeholder="请输入评价项目" />
</view>
<view class="content-input">
<view class="input-titile">流程说明</view>
<textarea auto-height class="input-con" v-model="fromValue.processDescription" placeholder="请输入流程说明" />
</view>
<view class="content-input">
<view class="input-titile">资质证书</view>
<button class="upload-btn" type="primary" size="mini" @click="handleResumeUpload" :loading="isUploading" :disabled="isUploading">
<uni-icons type="cloud-upload" size="20"></uni-icons>
<text class="upload-text">上传</text>
</button>
<view class="uploaded-file-info" v-if="fileListss.list.length>0">
<view v-for="item in fileListss.list" :key="item.url" >
<image class="file-icon" src="/static/icons/file-icon.png" mode="widthFix"></image>
<text class="file-name">{{ item.name }}</text>
<button class="delete-file-btn" size="mini" @click.stop="handleDeleteResume(item)">删除</button>
</view>
</view>
</view>
<view class="content-input">
<view class="input-titile">机构LOGO</view>
<button class="upload-btn" type="primary" size="mini" @click="handleResumeUpload2" :loading="isUploading2" :disabled="isUploading2">
<uni-icons type="cloud-upload" size="20"></uni-icons>
<text class="upload-text">上传</text>
</button>
<view class="uploaded-file-info" style="width: 200rpx;height: 200rpx;">
<img :src="uploadedResumeUrl" alt="" />
</view>
</view>
</view>
<view class="footer">
<view class="footerBtn" @click="confirm()">保存</view>
</view>
<SelectPopup ref="selectPopupRef"></SelectPopup>
</div>
</template>
<script setup>
import { reactive, inject, watch, ref, onMounted, onUnmounted } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
const { $api, navTo, navBack, checkingPhoneRegExp,config } = inject('globalFunction');
import { storeToRefs } from 'pinia';
import useUserStore from '@/stores/useUserStore';
import useDictStore from '@/stores/useDictStore';
import SelectPopup from '@/components/selectPopup/selectPopup.vue';
const { userInfo } = storeToRefs(useUserStore());
const { getUserResume } = useUserStore();
const dictStore = useDictStore();
const { dictLabel, oneDictData, complete: dictComplete, getDictSelectOption } = dictStore;
let fromValue = reactive({
organName: '',
qualificationGrade:'',
contactName:'',
organWorkStarttime:'',
organAddressArr:'',
address:'',
introduction:'',
location:'',
contactPhone: '',
processDescription:'',
evaluationItems:'',
});
const gradeLabels=ref([])
let gradeIndex=ref('')
let fileListss=reactive({
list:[],
})
let region=reactive({
provinceData:[
[],
[],
[]
],
arrayIndex:[0,0,0],
})
const isShow = ref(false); // 上传中状态
const isUploading = ref(false); // 上传中状态
const isUploading2 = ref(false); // 上传中状态
let uploadedResumeName = ref(''); // 已上传简历文件名
let uploadedResumeName2 = ref('');
let uploadedResumeUrl = ref(''); // 已上传
// 在onLoad中初始化数据确保页面加载时就能获取技能信息
onLoad((options = {}) => {
getRegion(1,650000000000)
getDictionary()
setTimeout(()=>{
getDetail()
},2000)
});
// 监听页面显示,接收从技能查询页面返回的数据
onShow(() => {
let params = uni.globalParams || {}; // 从全局变量获取参数
if(params.longitude&&params.latitude){
fromValue.location=params.longitude+','+params.latitude
}
uni.globalParams = null; // 清除全局变量,避免重复使用
// 通过事件总线接收技能选择结果
// uni.$on('skillSelected', handleSkillSelected);
});
// 页面卸载时移除事件监听
// onUnmounted(() => {
// uni.$off('skillSelected', handleSkillSelected);
// });
// 监听字典数据加载完成,自动更新学历显示
watch(() => region.provinceData[0], (newVal) => {
if (newVal&&isShow.value==false) {
getRegion(2,region.provinceData[0][0].dictValue)
}
});
watch(() => region.provinceData[1], (newVal) => {
if (newVal&&isShow.value==false) {
getRegion(3,region.provinceData[1][0].dictValue)
}
});
function getDetail(){
// if(userInfo.userId){
$api.myRequest("/train/public/rate/organ/getRateByUnifiedSocialCreditCode", {
tags: '913700001630477270',
}, "GET", 9100, {
Authorization: `Bearer ${uni.getStorageSync('token')}`
}).then((res) => {
if (res.code === 200) {
getRegion(2,res.data.organProvince)
getRegion(3,res.data.organCity)
fromValue = {
...fromValue,
...res.data,
location: `${res.data.longitude},${res.data.latitude}`||'',
organAddressArr: [
res.data.organProvince,
res.data.organCity,
res.data.organCounty,
],
};
isShow.value=true
gradeIndex.value=res.data.qualificationGrade==2?1:res.data.qualificationGrade==1?0:''
uploadedResumeName2.value = res.data.logo?(JSON.parse(res.data.logo))[0].name:'';
uploadedResumeUrl.value = res.data.logo?config.trainVideoImgUrl+(JSON.parse(res.data.logo))[0].url:'';
fileListss.list = JSON.parse(res.data.zhengshu) || [];
setTimeout(()=>{
region.provinceData[0].forEach((item,index)=>{
if(item.dictValue==res.data.organProvince){
region.arrayIndex[0]=index
}
})
region.provinceData[1].forEach((item,index)=>{
if(item.dictValue==res.data.organCity){
region.arrayIndex[1]=index
}
})
region.provinceData[2].forEach((item,index)=>{
if(item.dictValue==res.data.organCounty){
region.arrayIndex[2]=index
}
})
},2000)
}
})
// }else{
// $api.msg('请先登录');
// }
}
function getDictionary(){
$api.myRequest('/system/public/dict/data/type/qualification_grade_type', {},'get',9100).then((resData) => {
gradeLabels.value=resData.data;
});
}
function getRegion(level,value){
let header = {
'Authorization': uni.getStorageSync('token')||'',
'Content-Type': "application/x-www-form-urlencoded"
};
$api.myRequest('/system/public/dict/data/getByParentValue', {
dictType: "administrative_division",
dictParentValue: value ? value: "-1",
childFlag: (level&&level > 1) ? "0" : "1",
},'post',9100,header).then((resData) => {
if(resData.code==200){
if(level==1){
let row=resData.data.filter(item => item.dictLabel === '喀什地区');
region.provinceData[level-1]=row
}else{
region.provinceData[level-1]=resData.data
}
}
});
}
function bindPickerChange(e) {
gradeIndex.value = e.detail.value
}
function onWorkTypePickerChange(e){
region.arrayIndex=e.detail.value;
isShow.value=true
}
function onWorkTypeColumnChange(e){
const { column, value } = e.detail;
const newIndexes = [...region.arrayIndex];
newIndexes[column] = value;
// 重置后续列的数据
if (column === 0) {
// 第一列变化,重置第二、三列
const selectedLevel1 = region.provinceData[0][value];
if (selectedLevel1) {
getRegion(2,selectedLevel1.dictValue)
region.arrayIndex[1] = 0;
region.arrayIndex[2] = 0;
}
} else if (column === 1) {
// 第二列变化,重置第三列
const selectedLevel2 = this.workTypeColumns[1][value];
if (selectedLevel2) {
getRegion(3,selectedLevel2.dictValue)
region.arrayIndex[2] = 0;
}
}
this.workTypeIndexes = newIndexes;
}
const confirm = () => {
if (!fromValue.organName) {
return $api.msg('请输入机构名称');
}
if (!fromValue.contactName) {
return $api.msg('请输入联系人');
}
if (!fromValue.contactPhone) {
return $api.msg('请输入联系方式');
}
if (!checkingPhoneRegExp(fromValue.contactPhone)) {
return $api.msg('请输入正确联系方式');
}
if (!fromValue.address) {
return $api.msg('请输入机构地址');
}
const params = {
...fromValue,
organProvince: region.provinceData[0][region.arrayIndex[0]].dictValue, // 机构所在省
organCity: region.provinceData[1][region.arrayIndex[1]].dictValue, // 机构所在市
organCounty: region.provinceData[2][region.arrayIndex[2]].dictValue, // 机构所在区县
zhengshu: JSON.stringify(fileListss.list),
logo: JSON.stringify([{name:uploadedResumeName2.value,url:uploadedResumeUrl.value}]), // 机构logo
qualificationGrade:gradeLabels.value[gradeIndex.value].dictValue
};
$api.myRequest('/train/public/rate/organ/maintainRateOrgan', params, 'post').then((resData) => {
if(resData.code==200){
$api.msg(resData.msg);
setTimeout(()=>{
navBack();
},2000)
}
});
};
function selectLocation(){
uni.navigateTo({
url:'/packageB/components/map?location='+fromValue.location
})
}
function handleResumeUpload(){
// 从缓存获取用户ID参考首页实现方式
// 优先从store获取如果为空则从缓存获取
const storeUserId = userInfo.value?.userId;
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
const cachedUserId = cachedUserInfo.userId;
// 获取用户ID优先使用store中的userId如果store中没有使用缓存中的userId
const userId = storeUserId || cachedUserId;
if (!userId) {
$api.msg('请先登录');
return;
}
// 检查是否正在上传
if (isUploading.value) {
return;
}
// 选择文件(微信小程序使用 wx.chooseMessageFileuni-app 中对应 uni.chooseMessageFile
uni.chooseMessageFile({
count: 1, // 只能选择一个文件
type: 'file', // 选择任意文件类型
success: (res) => {
// 注意:文件路径在 res.tempFiles[0].path
const file = res.tempFiles[0];
const tempFilePath = file.path; // 获取临时文件路径
const fileName = file.name; // 获取文件名
uploadedResumeName.value=file.name
// 检查文件大小20MB = 20 * 1024 * 1024 字节)
const maxSize = 20 * 1024 * 1024;
if (file.size > maxSize) {
$api.msg('文件大小不能超过 20MB');
return;
}
// 开始上传
uploadResumeFile(tempFilePath, fileName, userId);
},
fail: (err) => {
console.error('选择文件失败:', err);
// 用户取消选择不提示错误
if (err.errMsg && !err.errMsg.includes('cancel')) {
$api.msg('选择文件失败,请重试');
}
}
});
};
// 上传简历文件到服务器(使用 wx.uploadFileuni-app 中对应 uni.uploadFile
function uploadResumeFile(filePath, fileName, userId){
// 确保 userId 存在且有效
if (!userId) {
// 如果传入的userId为空尝试从缓存再次获取
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
const cachedUserId = cachedUserInfo.userId;
if (!cachedUserId) {
$api.msg('用户ID不存在无法上传');
return;
}
// 使用缓存中的userId
userId = cachedUserId;
}
isUploading.value = true;
// 获取token从缓存获取参考首页实现方式
let Authorization = '';
const tokenValue = uni.getStorageSync('token') || '';
if (tokenValue) {
Authorization = tokenValue;
} else {
// 如果缓存中没有token尝试从store获取
const userStore = useUserStore();
if (userStore.token) {
Authorization = userStore.token;
}
}
// 根据接口文档bussinessId 应该作为 Query 参数传递,而不是 formData
// 将 bussinessId 拼接到 URL 上作为查询参数
const uploadUrl = `${config.baseUrl}/app/file/upload?bussinessId=${encodeURIComponent(String(userId))}`;
uni.uploadFile({
url: uploadUrl,
filePath: filePath,
name: 'file',
header: {
'Authorization': encodeURIComponent(Authorization)
},
// formData: {
// 'user': 'test'
// },
success: (uploadFileRes) => {
isUploading.value = false;
let resData;
if (typeof uploadFileRes.data === 'string') {
resData = JSON.parse(uploadFileRes.data);
} else {
resData = uploadFileRes.data;
}
// 判断上传是否成功
if (uploadFileRes.statusCode === 200 && resData.code === 200) {
ceshi(resData.msg)
// 上传成功,处理返回结果
$api.msg('上传成功');
} else {
// 上传失败
const errorMsg = resData.msg || resData.message || '上传失败,请重试';
$api.msg(errorMsg);
// console.error('上传失败:', resData);
}
},
fail: (err) => {
// 上传失败
console.error('上传文件失败:', err);
$api.msg('上传失败,请检查网络连接');
},
});
};
function ceshi(img){
isUploading.value = false;
let arr={name:uploadedResumeName.value,url:img || ''}
fileListss.list.push(arr)
};
// 删除已上传的简历
const handleDeleteResume = (item) => {
if (!uploadedResumeName.value) {
return;
}
uni.showModal({
title: '确认删除',
content: '确定要删除已上传的证书吗?',
success: (res) => {
if (res.confirm) {
var array = JSON.parse(JSON.stringify(fileListss.list));
var index = array.indexOf(item);
if (index > -1) {
array.splice(index, 1); // 从 index 处开始删除一个元素
}
fileListss.list=array
// 清除本地数据
uploadedResumeName.value = '';
$api.msg('已删除');
// 如果需要,可以调用后端接口删除服务器上的文件
// deleteResumeFile(userId);
}
}
});
};
function handleResumeUpload2(){
// 从缓存获取用户ID参考首页实现方式
// 优先从store获取如果为空则从缓存获取
const storeUserId = userInfo.value?.userId;
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
const cachedUserId = cachedUserInfo.userId;
// 获取用户ID优先使用store中的userId如果store中没有使用缓存中的userId
const userId = storeUserId || cachedUserId;
if (!userId) {
$api.msg('请先登录');
return;
}
// 检查是否正在上传
if (isUploading2.value) {
return;
}
// 选择文件(微信小程序使用 wx.chooseMessageFileuni-app 中对应 uni.chooseMessageFile
uni.chooseMessageFile({
count: 1, // 只能选择一个文件
type: 'file', // 选择任意文件类型
success: (res) => {
// 注意:文件路径在 res.tempFiles[0].path
const file = res.tempFiles[0];
const tempFilePath = file.path; // 获取临时文件路径
const fileName = file.name; // 获取文件名
uploadedResumeName2.value=file.name
// 检查文件大小20MB = 20 * 1024 * 1024 字节)
const maxSize = 20 * 1024 * 1024;
if (file.size > maxSize) {
$api.msg('文件大小不能超过 20MB');
return;
}
// 开始上传
uploadResumeFile2(tempFilePath, fileName, userId);
},
fail: (err) => {
console.error('选择文件失败:', err);
// 用户取消选择不提示错误
if (err.errMsg && !err.errMsg.includes('cancel')) {
$api.msg('选择文件失败,请重试');
}
}
});
};
// 上传简历文件到服务器(使用 wx.uploadFileuni-app 中对应 uni.uploadFile
function uploadResumeFile2(filePath, fileName, userId){
// 确保 userId 存在且有效
if (!userId) {
// 如果传入的userId为空尝试从缓存再次获取
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
const cachedUserId = cachedUserInfo.userId;
if (!cachedUserId) {
$api.msg('用户ID不存在无法上传');
return;
}
// 使用缓存中的userId
userId = cachedUserId;
}
isUploading2.value = true;
// 获取token从缓存获取参考首页实现方式
let Authorization = '';
const tokenValue = uni.getStorageSync('token') || '';
if (tokenValue) {
Authorization = tokenValue;
} else {
// 如果缓存中没有token尝试从store获取
const userStore = useUserStore();
if (userStore.token) {
Authorization = userStore.token;
}
}
// 根据接口文档bussinessId 应该作为 Query 参数传递,而不是 formData
// 将 bussinessId 拼接到 URL 上作为查询参数
const uploadUrl = `${config.baseUrl}/app/file/upload?bussinessId=${encodeURIComponent(String(userId))}`;
uni.uploadFile({
url: uploadUrl,
filePath: filePath,
name: 'file',
header: {
'Authorization': encodeURIComponent(Authorization)
},
// formData: {
// 'user': 'test'
// },
success: (uploadFileRes) => {
isUploading2.value = false;
let resData;
if (typeof uploadFileRes.data === 'string') {
resData = JSON.parse(uploadFileRes.data);
} else {
resData = uploadFileRes.data;
}
// 判断上传是否成功
if (uploadFileRes.statusCode === 200 && resData.code === 200) {
ceshi2(resData.msg)
// 上传成功,处理返回结果
$api.msg('上传成功');
} else {
// 上传失败
const errorMsg = resData.msg || resData.message || '上传失败,请重试';
$api.msg(errorMsg);
// console.error('上传失败:', resData);
}
},
fail: (err) => {
// 上传失败
console.error('上传文件失败:', err);
$api.msg('上传失败,请检查网络连接');
},
});
};
function ceshi2(img){
isUploading2.value = false;
uploadedResumeUrl.value=img;
};
</script>
<style lang="stylus" scoped>
.btn{
margin-top: -30rpx
}
.input-required{
color: red
}
/* 上传按钮 */
.upload-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 10rpx;
padding: 0 30rpx;
background-color: #f5f7fa;
color: #1677ff;
border-radius: 8rpx;
font-size: 26rpx;
margin-top: 20rpx;
}
.upload-icon {
width: 30rpx;
height: 30rpx;
}
.reupload-text {
font-size: 22rpx;
color: #666;
}
/* 上传说明文字 */
.upload-tip {
font-size: 20rpx;
color: #999;
text-align: center;
line-height: 1.4;
}
/* 已上传文件信息 */
.uploaded-file-info>view {
display: flex;
align-items: center;
gap: 15rpx;
padding: 15rpx 20rpx;
background-color: #fafafa;
border-radius: 8rpx;
margin-top: 10rpx;
}
.file-icon {
width: 36rpx;
height: 36rpx;
}
.file-name {
font-size: 24rpx;
color: #333;
max-width: 400rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.delete-file-btn {
padding: 0 15rpx;
height: 36rpx;
line-height: 36rpx;
font-size: 22rpx;
color: #ff4d4f;
background-color: transparent;
}
.content{
padding: 28rpx;
display: flex;
flex-direction: column;
justify-content: flex-start
height: calc(100% - 120rpx)
}
.footer{
width: 100%;
height: 120rpx;
background: #fff;
position: fixed;
z-index: 10;
bottom: 0;
left: 0;
display: flex;
align-content: center;
justify-content: center;
}
.footerBtn{
width: 90%;
height: 90rpx;
line-height: 90rpx;
text-align: center
border-radius: 10rpx;
color: #fff;
background: #409EFF;
}
.content-input
margin-bottom: 52rpx
.input-titile
font-weight: 400;
font-size: 28rpx;
color: #6A6A6A
.input-con
font-weight: 400;
font-size: 28rpx;
color: #333333;
line-height: 80rpx;
height: 80rpx;
border-bottom: 2rpx solid #EBEBEB
position: relative;
.error-message
color: #ff4757;
font-size: 24rpx;
margin-top: 10rpx;
line-height: 1.4;
.success-message
color: #2ed573;
font-size: 24rpx;
margin-top: 10rpx;
line-height: 1.4;
.input-error
.input-con
border-bottom-color: #ff4757;
.triangle::before
position: absolute;
right: 20rpx;
top: calc(50% - 2rpx);
content: '';
width: 4rpx;
height: 18rpx;
border-radius: 2rpx
background: #697279;
transform: translate(0, -50%) rotate(-45deg) ;
.triangle::after
position: absolute;
right: 20rpx;
top: 50%;
content: '';
width: 4rpx;
height: 18rpx;
border-radius: 2rpx
background: #697279;
transform: rotate(45deg)
.input-nx
position: relative
border-bottom: 2rpx solid #EBEBEB
padding-bottom: 30rpx
display: flex
flex-wrap: wrap
.nx-item
padding: 16rpx 24rpx
width: fit-content
border-radius: 20rpx
border: 2rpx solid #E8EAEE
background-color: #f8f9fa
margin-right: 16rpx
margin-top: 16rpx
font-size: 28rpx
color: #333333
transition: all 0.2s ease
&:hover
background-color: #e9ecef
border-color: #256bfa
color: #256bfa
.content-sex
height: 110rpx;
display: flex
justify-content: space-between;
align-items: flex-start;
border-bottom: 2rpx solid #EBEBEB
margin-bottom: 52rpx
.sex-titile
line-height: 80rpx;
.sext-ri
display: flex
align-items: center;
.sext-box
height: 76rpx;
width: 152rpx;
text-align: center;
line-height: 80rpx;
border-radius: 12rpx 12rpx 12rpx 12rpx
border: 2rpx solid #E8EAEE;
margin-left: 28rpx
font-weight: 400;
font-size: 28rpx;
.sext-boxactive
color: #256BFA
background: rgba(37,107,250,0.1);
border: 2rpx solid #256BFA;
.next-btn
width: 100%;
height: 90rpx;
background: #256BFA;
border-radius: 12rpx 12rpx 12rpx 12rpx;
font-weight: 500;
font-size: 32rpx;
color: #FFFFFF;
text-align: center;
line-height: 90rpx
// 技能信息样式
.content-skills
margin-bottom: 52rpx
.skills-header
display: flex
justify-content: space-between
align-items: center
margin-bottom: 32rpx
.input-titile
font-weight: 400
font-size: 28rpx
color: #6A6A6A
.add-skill-btn
padding: 16rpx 32rpx
background: #256BFA
color: #FFFFFF
border-radius: 8rpx
font-size: 26rpx
font-weight: 500
transition: all 0.3s ease
&:active
background: #1a5cd9
transform: scale(0.98)
&.disabled
background: #CCCCCC
color: #999999
cursor: not-allowed
&:active
background: #CCCCCC
transform: none
.skills-list
.skill-item
background: #FFFFFF
border: 2rpx solid #E8EAEE
border-radius: 12rpx
padding: 24rpx
margin-bottom: 24rpx
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05)
transition: all 0.3s ease
&:hover
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1)
border-color: #256BFA
.skill-header
display: flex
justify-content: space-between
align-items: center
margin-bottom: 20rpx
.skill-number
font-weight: 500
font-size: 28rpx
color: #333333
.skill-actions
.action-btn
padding: 8rpx 16rpx
border-radius: 6rpx
font-size: 24rpx
font-weight: 400
transition: all 0.2s ease
&.delete-btn
background: #FF4D4F
color: #FFFFFF
&:active
background: #D9363E
transform: scale(0.95)
.skill-fields
display: flex
flex-direction: column
gap: 20rpx
.skill-field
.field-label
font-weight: 400
font-size: 26rpx
color: #6A6A6A
margin-bottom: 8rpx
.field-input
font-weight: 400
font-size: 28rpx
color: #333333
line-height: 72rpx
height: 72rpx
border: 2rpx solid #E8EAEE
border-radius: 8rpx
padding: 0 20rpx
background: #F8F9FA
transition: all 0.3s ease
&:focus
border-color: #256BFA
background: #FFFFFF
&.triangle::before
right: 30rpx
top: calc(50% - 2rpx)
&.triangle::after
right: 30rpx
top: 50%
.empty-skills
text-align: center
padding: 60rpx 0
background: #F8F9FA
border-radius: 12rpx
border: 2rpx dashed #E8EAEE
.empty-text
font-size: 28rpx
color: #999999
font-weight: 400
</style>

View File

@@ -0,0 +1,986 @@
<template>
<div style="height: 90vh;overflow-y: auto;">
<view class="content">
<view class="content-input">
<view class="input-titile"><text class="input-required">*</text>机构名称</view>
<input class="input-con" v-model="fromValue.organName" placeholder="请输入机构名称" />
</view>
<view class="content-input">
<view class="input-titile"><text class="input-required">*</text>联系人</view>
<input class="input-con" v-model="fromValue.contactName" placeholder="请输入联系人" />
</view>
<view class="content-input">
<view class="input-titile">联系方式</view>
<input class="input-con" v-model="fromValue.contactPhone" placeholder="请输入您的联系方式" />
</view>
<view class="content-input">
<view class="input-titile">机构类别</view>
<input class="input-con" v-model="fromValue.organType" placeholder="请输入机构类别" />
</view>
<view class="content-input">
<view class="input-titile">机构性质</view>
<input class="input-con" v-model="fromValue.organNature" placeholder="请输入机构性质" />
</view>
<view class="content-input">
<view class="input-titile">培训级别及证书类型</view>
<input class="input-con" v-model="fromValue.trainType" placeholder="请输入培训级别及证书类型" />
</view>
<view class="content-input">
<view class="input-titile">办学许可证号/批文号</view>
<input class="input-con" v-model="fromValue.organLicense" placeholder="请输入办学许可证号/批文号" />
</view>
<view class="content-input">
<view class="input-titile">所在区域</view>
<view class="input-con">
<picker mode = 'multiSelector'
@change="onWorkTypePickerChange"
@columnchange="onWorkTypeColumnChange"
range-key="dictLabel"
:value="fromValue.organAddressArr"
:range="region.provinceData">
<view class="uni-input" v-if="isShow" style="width: 100%;">{{region.provinceData[0][region.arrayIndex[0]].dictLabel}}/{{region.provinceData[1][region.arrayIndex[1]].dictLabel}}/{{region.provinceData[2].length>0?region.provinceData[2][region.arrayIndex[2]].dictLabel:''}}</view>
<view class="uni-input" style="width: 100%;" v-else>请选择</view>
</picker>
</view>
<!-- <input class="input-con" v-model="fromValue.organAddressArr" placeholder="请选择" /> -->
</view>
<view class="content-input">
<view class="input-titile">机构地址</view>
<input class="input-con" v-model="fromValue.address" placeholder="请输入机构地址" />
</view>
<view class="content-input">
<view class="input-titile">地图定位(经纬度)</view>
<view @click="selectLocation" class="input-con">{{fromValue.location?fromValue.location:'请选择'}}</view>
</view>
<view class="content-input">
<view class="input-titile"><text class="input-required">*</text>统一社会信用代码</view>
<input class="input-con" v-model="fromValue.unifiedSocialCreditCode" placeholder="请输入统一社会信用代码" />
</view>
<view class="content-input">
<view class="input-titile">机构简介</view>
<textarea auto-height class="input-con" v-model="fromValue.introduction" placeholder="请输入机构简介" />
</view>
<view class="content-input">
<view class="input-titile">培训项目</view>
<checkbox-group @change="checkboxChange">
<view style="display: flex;flex-wrap: wrap;">
<view v-for="item in courseLabels" :key="item.dictCode" style="margin-right: 20rpx;margin-bottom: 20rpx;">
<label>
<checkbox :value="item.dictCode" :checked="item.checked" />
{{item.dictLabel}}
</label>
</view>
</view>
</checkbox-group>
</view>
<view class="content-input">
<view class="input-titile">资质证书</view>
<button class="upload-btn" type="primary" size="mini" @click="handleResumeUpload" :loading="isUploading" :disabled="isUploading">
<uni-icons type="cloud-upload" size="20"></uni-icons>
<text class="upload-text">上传</text>
</button>
<view class="uploaded-file-info" v-if="fileListss.list.length>0">
<view v-for="item in fileListss.list" :key="item.url" >
<image class="file-icon" src="/static/icons/file-icon.png" mode="widthFix"></image>
<text class="file-name">{{ item.name }}</text>
<button class="delete-file-btn" size="mini" @click.stop="handleDeleteResume(item)">删除</button>
</view>
</view>
</view>
<view class="content-input">
<view class="input-titile">机构LOGO</view>
<button class="upload-btn" type="primary" size="mini" @click="handleResumeUpload2" :loading="isUploading2" :disabled="isUploading2">
<uni-icons type="cloud-upload" size="20"></uni-icons>
<text class="upload-text">上传</text>
</button>
<view class="uploaded-file-info" style="width: 200rpx;height: 200rpx;">
<img :src="uploadedResumeUrl" alt="" />
</view>
</view>
</view>
<view class="footer">
<view class="footerBtn" @click="confirm()">保存</view>
</view>
<SelectPopup ref="selectPopupRef"></SelectPopup>
</div>
</template>
<script setup>
import { reactive, inject, watch, ref, onMounted, onUnmounted } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
const { $api, navTo, navBack, checkingPhoneRegExp,config } = inject('globalFunction');
import { storeToRefs } from 'pinia';
import useUserStore from '@/stores/useUserStore';
import useDictStore from '@/stores/useDictStore';
import SelectPopup from '@/components/selectPopup/selectPopup.vue';
const { userInfo } = storeToRefs(useUserStore());
const { getUserResume } = useUserStore();
const dictStore = useDictStore();
const { dictLabel, oneDictData, complete: dictComplete, getDictSelectOption } = dictStore;
let fromValue = reactive({
organName: '',
contactName:'',
organType:'',
organNature:'',
trainType:'',
organLicense:'',
organAddressArr:'',
address:'',
unifiedSocialCreditCode:'',
introduction:'',
location:'',
longitude:'',
latitude:'',
education: '',
politicalAffiliation: '',
contactPhone: ''
});
const courseLabels=ref([])
let fileListss=reactive({
list:[],
})
let region=reactive({
provinceData:[
[],
[],
[]
],
arrayIndex:[0,0,0],
})
const isShow = ref(false); // 上传中状态
const isUploading = ref(false); // 上传中状态
const isUploading2 = ref(false); // 上传中状态
const uploadedResumeName = ref('');
let uploadedResumeName2 = ref('');
let uploadedResumeUrl = ref(''); // 已上传
// 在onLoad中初始化数据确保页面加载时就能获取技能信息
onLoad((options = {}) => {
getRegion(1,650000000000)
getDictionary()
setTimeout(()=>{
getDetail()
},2000)
});
// 监听页面显示,接收从技能查询页面返回的数据
onShow(() => {
let params = uni.globalParams || {}; // 从全局变量获取参数
if(params.longitude&&params.latitude){
fromValue.location=params.longitude+','+params.latitude
fromValue.longitude=params.longitude
fromValue.latitude=params.latitude
}
uni.globalParams = null; // 清除全局变量,避免重复使用
// 通过事件总线接收技能选择结果
// uni.$on('skillSelected', handleSkillSelected);
});
// 页面卸载时移除事件监听
// onUnmounted(() => {
// uni.$off('skillSelected', handleSkillSelected);
// });
// 监听字典数据加载完成,自动更新学历显示
watch(() => region.provinceData[0], (newVal) => {
if (newVal&&isShow.value==false) {
getRegion(2,region.provinceData[0][0].dictValue)
}
});
watch(() => region.provinceData[1], (newVal) => {
if (newVal&&isShow.value==false) {
getRegion(3,region.provinceData[1][0].dictValue)
}
});
function getDetail(){
// if(userInfo.userId){
$api.myRequest("/train/public/train/organ/getTrainByUnifiedSocialCreditCode", {
tags: "913700001630477270",
}, "GET", 9100, {
Authorization: `Bearer ${uni.getStorageSync('token')}`,
'Content-Type': "application/x-www-form-urlencoded"
}).then((res) => {
if (res.code === 200) {
getRegion(2,res.data.organProvince)
getRegion(3,res.data.organCity)
fromValue = {
...fromValue,
...res.data,
location: `${res.data.longitude},${res.data.latitude}`||'',
organAddressArr: [
res.data.organProvince,
res.data.organCity,
res.data.organCounty,
],
};
let courseLists=res.data.course.split(",")
courseLists.forEach(item=>{
courseLabels.value.forEach(val=>{
if(item==val.dictValue){
val.checked=true
}
})
})
isShow.value=true
uploadedResumeName2.value = res.data.logo?(JSON.parse(res.data.logo))[0].name:'';
uploadedResumeUrl.value = res.data.logo?config.trainVideoImgUrl+(JSON.parse(res.data.logo))[0].url:'';
fileListss.list = JSON.parse(res.data.zhengshu) || [];
setTimeout(()=>{
region.provinceData[0].forEach((item,index)=>{
if(item.dictValue==res.data.organProvince){
region.arrayIndex[0]=index
}
})
region.provinceData[1].forEach((item,index)=>{
if(item.dictValue==res.data.organCity){
region.arrayIndex[1]=index
}
})
region.provinceData[2].forEach((item,index)=>{
if(item.dictValue==res.data.organCounty){
region.arrayIndex[2]=index
}
})
},2000)
}
})
// }else{
// $api.msg('请先登录');
// }
}
function getDictionary(){
$api.myRequest('/system/public/dict/data/type/course_type', {},'get',9100).then((resData) => {
courseLabels.value=resData.data;
courseLabels.value.forEach(item=>{
item.checked=false;
})
});
}
function getRegion(level,value){
let header = {
'Authorization': uni.getStorageSync('token')||'',
'Content-Type': "application/x-www-form-urlencoded"
};
$api.myRequest('/system/public/dict/data/getByParentValue', {
dictType: "administrative_division",
dictParentValue: value ? value: "-1",
childFlag: (level&&level > 1) ? "0" : "1",
},'post',9100,header).then((resData) => {
if(resData.code==200){
region.provinceData[level-1]=resData.data
}
});
}
function onWorkTypePickerChange(e){
region.arrayIndex=e.detail.value;
isShow.value=true
}
function onWorkTypeColumnChange(e){
const { column, value } = e.detail;
const newIndexes = [...region.arrayIndex];
newIndexes[column] = value;
// 重置后续列的数据
if (column === 0) {
// 第一列变化,重置第二、三列
const selectedLevel1 = region.provinceData[0][value];
if (selectedLevel1) {
getRegion(2,selectedLevel1.dictValue)
region.arrayIndex[1] = 0;
region.arrayIndex[2] = 0;
}
} else if (column === 1) {
// 第二列变化,重置第三列
const selectedLevel2 = this.workTypeColumns[1][value];
if (selectedLevel2) {
getRegion(3,selectedLevel2.dictValue)
region.arrayIndex[2] = 0;
}
}
this.workTypeIndexes = newIndexes;
}
const confirm = () => {
if (!fromValue.organName) {
return $api.msg('请输入机构名称');
}
if (!fromValue.contactName) {
return $api.msg('请输入联系人');
}
if (!fromValue.contactPhone) {
return $api.msg('请输入联系方式');
}
if (!checkingPhoneRegExp(fromValue.contactPhone)) {
return $api.msg('请输入正确联系方式');
}
if (!fromValue.unifiedSocialCreditCode) {
return $api.msg('请输入统一社会信用代码');
}
fromValue.organAddressArr=[
region.provinceData[0][region.arrayIndex[0]].dictValue,
region.provinceData[1][region.arrayIndex[1]].dictValue,
region.provinceData[2][region.arrayIndex[2]].dictValue
]
let courseList=[]
courseLabels.value.forEach(item=>{
if(item.checked==true){
courseList.push(item.dictValue)
}
})
const params = {
...fromValue,
organProvince: region.provinceData[0][region.arrayIndex[0]].dictValue, // 机构所在省
organCity: region.provinceData[1][region.arrayIndex[1]].dictValue, // 机构所在市
organCounty: region.provinceData[2][region.arrayIndex[2]].dictValue, // 机构所在区县
zhengshu: JSON.stringify(fileListss.list),
course: courseList.join(","), // 培训项目
logo: JSON.stringify([{name:uploadedResumeName2.value,url:uploadedResumeUrl.value}]), // 机构logo
};
$api.myRequest('/train/public/train/organ/maintainTrainOrgan', params, 'post').then((resData) => {
if(resData.code==200){
$api.msg(resData.msg);
setTimeout(()=>{
navBack();
},2000)
}
});
};
function checkboxChange(e){
var items = courseLabels.value,
values = e.detail.value;
for (var i = 0, lenI = items.length; i < lenI; ++i) {
const item = items[i]
if(values.includes(item.value)){
this.$set(item,'checked',true)
}else{
this.$set(item,'checked',false)
}
}
}
function selectLocation(){
uni.navigateTo({
url:'/packageB/components/map?location='+fromValue.location
})
}
function handleResumeUpload(){
// 从缓存获取用户ID参考首页实现方式
// 优先从store获取如果为空则从缓存获取
const storeUserId = userInfo.value?.userId;
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
const cachedUserId = cachedUserInfo.userId;
// 获取用户ID优先使用store中的userId如果store中没有使用缓存中的userId
const userId = storeUserId || cachedUserId;
if (!userId) {
$api.msg('请先登录');
return;
}
// 检查是否正在上传
if (isUploading.value) {
return;
}
// 选择文件(微信小程序使用 wx.chooseMessageFileuni-app 中对应 uni.chooseMessageFile
uni.chooseMessageFile({
count: 1, // 只能选择一个文件
type: 'file', // 选择任意文件类型
success: (res) => {
// 注意:文件路径在 res.tempFiles[0].path
const file = res.tempFiles[0];
const tempFilePath = file.path; // 获取临时文件路径
const fileName = file.name; // 获取文件名
uploadedResumeName.value=file.name
// 检查文件大小20MB = 20 * 1024 * 1024 字节)
const maxSize = 20 * 1024 * 1024;
if (file.size > maxSize) {
$api.msg('文件大小不能超过 20MB');
return;
}
// 开始上传
uploadResumeFile(tempFilePath, fileName, userId);
},
fail: (err) => {
console.error('选择文件失败:', err);
// 用户取消选择不提示错误
if (err.errMsg && !err.errMsg.includes('cancel')) {
$api.msg('选择文件失败,请重试');
}
}
});
};
// 上传简历文件到服务器(使用 wx.uploadFileuni-app 中对应 uni.uploadFile
function uploadResumeFile(filePath, fileName, userId){
// 确保 userId 存在且有效
if (!userId) {
// 如果传入的userId为空尝试从缓存再次获取
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
const cachedUserId = cachedUserInfo.userId;
if (!cachedUserId) {
$api.msg('用户ID不存在无法上传');
return;
}
// 使用缓存中的userId
userId = cachedUserId;
}
isUploading.value = true;
// 获取token从缓存获取参考首页实现方式
let Authorization = '';
const tokenValue = uni.getStorageSync('token') || '';
if (tokenValue) {
Authorization = tokenValue;
} else {
// 如果缓存中没有token尝试从store获取
const userStore = useUserStore();
if (userStore.token) {
Authorization = userStore.token;
}
}
// 根据接口文档bussinessId 应该作为 Query 参数传递,而不是 formData
// 将 bussinessId 拼接到 URL 上作为查询参数
const uploadUrl = `${config.baseUrl}/app/file/upload?bussinessId=${encodeURIComponent(String(userId))}`;
uni.uploadFile({
url: uploadUrl,
filePath: filePath,
name: 'file',
header: {
'Authorization': encodeURIComponent(Authorization)
},
// formData: {
// 'user': 'test'
// },
success: (uploadFileRes) => {
isUploading.value = false;
let resData;
if (typeof uploadFileRes.data === 'string') {
resData = JSON.parse(uploadFileRes.data);
} else {
resData = uploadFileRes.data;
}
// 判断上传是否成功
if (uploadFileRes.statusCode === 200 && resData.code === 200) {
ceshi(resData.msg)
// 上传成功,处理返回结果
$api.msg('上传成功');
} else {
// 上传失败
const errorMsg = resData.msg || resData.message || '上传失败,请重试';
$api.msg(errorMsg);
// console.error('上传失败:', resData);
}
},
fail: (err) => {
// 上传失败
console.error('上传文件失败:', err);
$api.msg('上传失败,请检查网络连接');
},
});
};
function ceshi(img){
isUploading.value = false;
let arr={name:uploadedResumeName.value,url:img || ''}
fileListss.list.push(arr)
};
// 删除已上传的简历
const handleDeleteResume = (item) => {
if (!uploadedResumeName.value) {
return;
}
uni.showModal({
title: '确认删除',
content: '确定要删除已上传的证书吗?',
success: (res) => {
if (res.confirm) {
var array = JSON.parse(JSON.stringify(fileListss.list));
var index = array.indexOf(item);
if (index > -1) {
array.splice(index, 1); // 从 index 处开始删除一个元素
}
fileListss.list=array
// 清除本地数据
uploadedResumeName.value = '';
$api.msg('已删除');
// 如果需要,可以调用后端接口删除服务器上的文件
// deleteResumeFile(userId);
}
}
});
};
function handleResumeUpload2(){
// 从缓存获取用户ID参考首页实现方式
// 优先从store获取如果为空则从缓存获取
const storeUserId = userInfo.value?.userId;
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
const cachedUserId = cachedUserInfo.userId;
// 获取用户ID优先使用store中的userId如果store中没有使用缓存中的userId
const userId = storeUserId || cachedUserId;
if (!userId) {
$api.msg('请先登录');
return;
}
// 检查是否正在上传
if (isUploading2.value) {
return;
}
// 选择文件(微信小程序使用 wx.chooseMessageFileuni-app 中对应 uni.chooseMessageFile
uni.chooseMessageFile({
count: 1, // 只能选择一个文件
type: 'file', // 选择任意文件类型
success: (res) => {
// 注意:文件路径在 res.tempFiles[0].path
const file = res.tempFiles[0];
const tempFilePath = file.path; // 获取临时文件路径
const fileName = file.name; // 获取文件名
uploadedResumeName2.value=file.name
// 检查文件大小20MB = 20 * 1024 * 1024 字节)
const maxSize = 20 * 1024 * 1024;
if (file.size > maxSize) {
$api.msg('文件大小不能超过 20MB');
return;
}
// 开始上传
uploadResumeFile2(tempFilePath, fileName, userId);
},
fail: (err) => {
console.error('选择文件失败:', err);
// 用户取消选择不提示错误
if (err.errMsg && !err.errMsg.includes('cancel')) {
$api.msg('选择文件失败,请重试');
}
}
});
};
// 上传简历文件到服务器(使用 wx.uploadFileuni-app 中对应 uni.uploadFile
function uploadResumeFile2(filePath, fileName, userId){
// 确保 userId 存在且有效
if (!userId) {
// 如果传入的userId为空尝试从缓存再次获取
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
const cachedUserId = cachedUserInfo.userId;
if (!cachedUserId) {
$api.msg('用户ID不存在无法上传');
return;
}
// 使用缓存中的userId
userId = cachedUserId;
}
isUploading2.value = true;
// 获取token从缓存获取参考首页实现方式
let Authorization = '';
const tokenValue = uni.getStorageSync('token') || '';
if (tokenValue) {
Authorization = tokenValue;
} else {
// 如果缓存中没有token尝试从store获取
const userStore = useUserStore();
if (userStore.token) {
Authorization = userStore.token;
}
}
// 根据接口文档bussinessId 应该作为 Query 参数传递,而不是 formData
// 将 bussinessId 拼接到 URL 上作为查询参数
const uploadUrl = `${config.baseUrl}/app/file/upload?bussinessId=${encodeURIComponent(String(userId))}`;
uni.uploadFile({
url: uploadUrl,
filePath: filePath,
name: 'file',
header: {
'Authorization': encodeURIComponent(Authorization)
},
// formData: {
// 'user': 'test'
// },
success: (uploadFileRes) => {
isUploading2.value = false;
let resData;
if (typeof uploadFileRes.data === 'string') {
resData = JSON.parse(uploadFileRes.data);
} else {
resData = uploadFileRes.data;
}
// 判断上传是否成功
if (uploadFileRes.statusCode === 200 && resData.code === 200) {
ceshi2(resData.msg)
// 上传成功,处理返回结果
$api.msg('上传成功');
} else {
// 上传失败
const errorMsg = resData.msg || resData.message || '上传失败,请重试';
$api.msg(errorMsg);
// console.error('上传失败:', resData);
}
},
fail: (err) => {
// 上传失败
console.error('上传文件失败:', err);
$api.msg('上传失败,请检查网络连接');
},
});
};
function ceshi2(img){
isUploading2.value = false;
uploadedResumeUrl.value=img;
};
</script>
<style lang="stylus" scoped>
.btn{
margin-top: -30rpx
}
.input-required{
color: red
}
/* 上传按钮 */
.upload-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 10rpx;
padding: 0 30rpx;
background-color: #f5f7fa;
color: #1677ff;
border-radius: 8rpx;
font-size: 26rpx;
margin-top: 20rpx;
}
.upload-icon {
width: 30rpx;
height: 30rpx;
}
.reupload-text {
font-size: 22rpx;
color: #666;
}
/* 上传说明文字 */
.upload-tip {
font-size: 20rpx;
color: #999;
text-align: center;
line-height: 1.4;
}
/* 已上传文件信息 */
.uploaded-file-info>view {
display: flex;
align-items: center;
gap: 15rpx;
padding: 15rpx 20rpx;
background-color: #fafafa;
border-radius: 8rpx;
margin-top: 10rpx;
}
.file-icon {
width: 36rpx;
height: 36rpx;
}
.file-name {
font-size: 24rpx;
color: #333;
max-width: 400rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.delete-file-btn {
padding: 0 15rpx;
height: 36rpx;
line-height: 36rpx;
font-size: 22rpx;
color: #ff4d4f;
background-color: transparent;
}
.content{
padding: 28rpx;
display: flex;
flex-direction: column;
justify-content: flex-start
height: calc(100% - 120rpx)
}
.footer{
width: 100%;
height: 120rpx;
background: #fff;
position: fixed;
z-index: 10;
bottom: 0;
left: 0;
display: flex;
align-content: center;
justify-content: center;
}
.footerBtn{
width: 90%;
height: 90rpx;
line-height: 90rpx;
text-align: center
border-radius: 10rpx;
color: #fff;
background: #409EFF;
}
.content-input
margin-bottom: 52rpx
.input-titile
font-weight: 400;
font-size: 28rpx;
color: #6A6A6A
.input-con
font-weight: 400;
font-size: 32rpx;
color: #333333;
line-height: 80rpx;
height: 80rpx;
border-bottom: 2rpx solid #EBEBEB
position: relative;
.error-message
color: #ff4757;
font-size: 24rpx;
margin-top: 10rpx;
line-height: 1.4;
.success-message
color: #2ed573;
font-size: 24rpx;
margin-top: 10rpx;
line-height: 1.4;
.input-error
.input-con
border-bottom-color: #ff4757;
.triangle::before
position: absolute;
right: 20rpx;
top: calc(50% - 2rpx);
content: '';
width: 4rpx;
height: 18rpx;
border-radius: 2rpx
background: #697279;
transform: translate(0, -50%) rotate(-45deg) ;
.triangle::after
position: absolute;
right: 20rpx;
top: 50%;
content: '';
width: 4rpx;
height: 18rpx;
border-radius: 2rpx
background: #697279;
transform: rotate(45deg)
.input-nx
position: relative
border-bottom: 2rpx solid #EBEBEB
padding-bottom: 30rpx
display: flex
flex-wrap: wrap
.nx-item
padding: 16rpx 24rpx
width: fit-content
border-radius: 20rpx
border: 2rpx solid #E8EAEE
background-color: #f8f9fa
margin-right: 16rpx
margin-top: 16rpx
font-size: 28rpx
color: #333333
transition: all 0.2s ease
&:hover
background-color: #e9ecef
border-color: #256bfa
color: #256bfa
.content-sex
height: 110rpx;
display: flex
justify-content: space-between;
align-items: flex-start;
border-bottom: 2rpx solid #EBEBEB
margin-bottom: 52rpx
.sex-titile
line-height: 80rpx;
.sext-ri
display: flex
align-items: center;
.sext-box
height: 76rpx;
width: 152rpx;
text-align: center;
line-height: 80rpx;
border-radius: 12rpx 12rpx 12rpx 12rpx
border: 2rpx solid #E8EAEE;
margin-left: 28rpx
font-weight: 400;
font-size: 28rpx;
.sext-boxactive
color: #256BFA
background: rgba(37,107,250,0.1);
border: 2rpx solid #256BFA;
.next-btn
width: 100%;
height: 90rpx;
background: #256BFA;
border-radius: 12rpx 12rpx 12rpx 12rpx;
font-weight: 500;
font-size: 32rpx;
color: #FFFFFF;
text-align: center;
line-height: 90rpx
// 技能信息样式
.content-skills
margin-bottom: 52rpx
.skills-header
display: flex
justify-content: space-between
align-items: center
margin-bottom: 32rpx
.input-titile
font-weight: 400
font-size: 28rpx
color: #6A6A6A
.add-skill-btn
padding: 16rpx 32rpx
background: #256BFA
color: #FFFFFF
border-radius: 8rpx
font-size: 26rpx
font-weight: 500
transition: all 0.3s ease
&:active
background: #1a5cd9
transform: scale(0.98)
&.disabled
background: #CCCCCC
color: #999999
cursor: not-allowed
&:active
background: #CCCCCC
transform: none
.skills-list
.skill-item
background: #FFFFFF
border: 2rpx solid #E8EAEE
border-radius: 12rpx
padding: 24rpx
margin-bottom: 24rpx
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05)
transition: all 0.3s ease
&:hover
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1)
border-color: #256BFA
.skill-header
display: flex
justify-content: space-between
align-items: center
margin-bottom: 20rpx
.skill-number
font-weight: 500
font-size: 28rpx
color: #333333
.skill-actions
.action-btn
padding: 8rpx 16rpx
border-radius: 6rpx
font-size: 24rpx
font-weight: 400
transition: all 0.2s ease
&.delete-btn
background: #FF4D4F
color: #FFFFFF
&:active
background: #D9363E
transform: scale(0.95)
.skill-fields
display: flex
flex-direction: column
gap: 20rpx
.skill-field
.field-label
font-weight: 400
font-size: 26rpx
color: #6A6A6A
margin-bottom: 8rpx
.field-input
font-weight: 400
font-size: 28rpx
color: #333333
line-height: 72rpx
height: 72rpx
border: 2rpx solid #E8EAEE
border-radius: 8rpx
padding: 0 20rpx
background: #F8F9FA
transition: all 0.3s ease
&:focus
border-color: #256BFA
background: #FFFFFF
&.triangle::before
right: 30rpx
top: calc(50% - 2rpx)
&.triangle::after
right: 30rpx
top: 50%
.empty-skills
text-align: center
padding: 60rpx 0
background: #F8F9FA
border-radius: 12rpx
border: 2rpx dashed #E8EAEE
.empty-text
font-size: 28rpx
color: #999999
font-weight: 400
</style>

View File

@@ -0,0 +1,469 @@
<template>
<div style="height: 90vh;overflow-y: auto;">
<view class="content">
<view class="content-input">
<view class="input-titile"><text class="input-required">*</text>标题</view>
<input class="input-con" v-model="fromValue.title" placeholder="请输入标题" />
</view>
<view class="content-input">
<view class="input-titile"><text class="input-required">*</text>联系人</view>
<!-- <input class="input-con" v-model="fromValue.contactName" placeholder="请输入联系人" /> -->
<unieditor @init-data="receiveInitData"/>
</view>
<view class="content-input">
<view class="input-titile">备注</view>
<textarea auto-height class="input-con" v-model="fromValue.remark" placeholder="请输入备注" />
</view>
</view>
<view class="footer">
<view class="footerBtn" @click="confirm()">保存</view>
</view>
<SelectPopup ref="selectPopupRef"></SelectPopup>
</div>
</template>
<script setup>
import { reactive, inject, watch, ref, onMounted, onUnmounted } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
const { $api, navTo, navBack, checkingPhoneRegExp,config } = inject('globalFunction');
import { storeToRefs } from 'pinia';
import useUserStore from '@/stores/useUserStore';
import useDictStore from '@/stores/useDictStore';
import SelectPopup from '@/components/selectPopup/selectPopup.vue';
import unieditor from '../../components/unieditor.vue'
const { userInfo } = storeToRefs(useUserStore());
const { getUserResume } = useUserStore();
const dictStore = useDictStore();
const { dictLabel, oneDictData, complete: dictComplete, getDictSelectOption } = dictStore;
let fromValue = reactive({
title: '',
content:'',
remark:'',
entProvince:'',
entCounty:'',
entCity:'',
status:'0',
});
let type=ref('add')
onLoad((options = {}) => {
});
// 监听页面显示,接收从技能查询页面返回的数据
onShow(() => {
getDetail()
// 通过事件总线接收技能选择结果
// uni.$on('skillSelected', handleSkillSelected);
});
// 页面卸载时移除事件监听
// onUnmounted(() => {
// uni.$off('skillSelected', handleSkillSelected);
// });
// 监听字典数据加载完成,自动更新学历显示
const receiveInitData = (initData) => {
fromValue.content=initData.value;
console.log('子组件初始化数据:', initData)
}
function getDetail(){
// if(userInfo.userId){
$api.myRequest("/train/public/train/organ/getTrainByUnifiedSocialCreditCode", {
tags: "913700001630477270",
}, "GET", 9100, {
Authorization: `Bearer ${uni.getStorageSync('token')}`,
'Content-Type': "application/x-www-form-urlencoded"
}).then((res) => {
if (res.code === 200) {
fromValue.entProvince = res.data.organProvince;
fromValue.entCounty = res.data.organCounty;
fromValue.entCity = res.data.organCity;
}
})
// }else{
// $api.msg('请先登录');
// }
}
const confirm = () => {
if (!fromValue.title) {
return $api.msg('请输入标题');
}
if (!fromValue.content) {
return $api.msg('请输入内容');
}
const params = {
...fromValue,
};
if(type.value=='add'){
$api.myRequest('/train/public/announcement/add', {
...fromValue,
type:2
}, 'post', 9100, {
Authorization: `Bearer ${uni.getStorageSync('token')}`,
'Content-Type': "application/x-www-form-urlencoded"
}).then((resData) => {
if(resData.code==200){
$api.msg('新增成功');
setTimeout(()=>{
navBack();
},2000)
}
});
} else if (type.value=='edit'){
$api.myRequest('/train/public/announcement/update', {
...fromValue,
type:2
}, 'post', 9100, {
Authorization: `Bearer ${uni.getStorageSync('token')}`,
'Content-Type': "application/x-www-form-urlencoded"
}).then((resData) => {
if(resData.code==200){
$api.msg('更新成功');
setTimeout(()=>{
navBack();
},2000)
}
});
}
};
</script>
<style lang="stylus" scoped>
.btn{
margin-top: -30rpx
}
.input-required{
color: red
}
/* 上传按钮 */
.upload-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 10rpx;
padding: 0 30rpx;
background-color: #f5f7fa;
color: #1677ff;
border-radius: 8rpx;
font-size: 26rpx;
margin-top: 20rpx;
}
.upload-icon {
width: 30rpx;
height: 30rpx;
}
.reupload-text {
font-size: 22rpx;
color: #666;
}
/* 上传说明文字 */
.upload-tip {
font-size: 20rpx;
color: #999;
text-align: center;
line-height: 1.4;
}
/* 已上传文件信息 */
.uploaded-file-info>view {
display: flex;
align-items: center;
gap: 15rpx;
padding: 15rpx 20rpx;
background-color: #fafafa;
border-radius: 8rpx;
margin-top: 10rpx;
}
.file-icon {
width: 36rpx;
height: 36rpx;
}
.file-name {
font-size: 24rpx;
color: #333;
max-width: 400rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.delete-file-btn {
padding: 0 15rpx;
height: 36rpx;
line-height: 36rpx;
font-size: 22rpx;
color: #ff4d4f;
background-color: transparent;
}
.content{
padding: 28rpx;
display: flex;
flex-direction: column;
justify-content: flex-start
height: calc(100% - 120rpx)
}
.footer{
width: 100%;
height: 120rpx;
background: #fff;
position: fixed;
z-index: 10;
bottom: 0;
left: 0;
display: flex;
align-content: center;
justify-content: center;
}
.footerBtn{
width: 90%;
height: 90rpx;
line-height: 90rpx;
text-align: center
border-radius: 10rpx;
color: #fff;
background: #409EFF;
}
.content-input
margin-bottom: 52rpx
.input-titile
font-weight: 400;
font-size: 28rpx;
color: #6A6A6A
.input-con
font-weight: 400;
font-size: 32rpx;
color: #333333;
line-height: 80rpx;
height: 80rpx;
border-bottom: 2rpx solid #EBEBEB
position: relative;
.error-message
color: #ff4757;
font-size: 24rpx;
margin-top: 10rpx;
line-height: 1.4;
.success-message
color: #2ed573;
font-size: 24rpx;
margin-top: 10rpx;
line-height: 1.4;
.input-error
.input-con
border-bottom-color: #ff4757;
.triangle::before
position: absolute;
right: 20rpx;
top: calc(50% - 2rpx);
content: '';
width: 4rpx;
height: 18rpx;
border-radius: 2rpx
background: #697279;
transform: translate(0, -50%) rotate(-45deg) ;
.triangle::after
position: absolute;
right: 20rpx;
top: 50%;
content: '';
width: 4rpx;
height: 18rpx;
border-radius: 2rpx
background: #697279;
transform: rotate(45deg)
.input-nx
position: relative
border-bottom: 2rpx solid #EBEBEB
padding-bottom: 30rpx
display: flex
flex-wrap: wrap
.nx-item
padding: 16rpx 24rpx
width: fit-content
border-radius: 20rpx
border: 2rpx solid #E8EAEE
background-color: #f8f9fa
margin-right: 16rpx
margin-top: 16rpx
font-size: 28rpx
color: #333333
transition: all 0.2s ease
&:hover
background-color: #e9ecef
border-color: #256bfa
color: #256bfa
.content-sex
height: 110rpx;
display: flex
justify-content: space-between;
align-items: flex-start;
border-bottom: 2rpx solid #EBEBEB
margin-bottom: 52rpx
.sex-titile
line-height: 80rpx;
.sext-ri
display: flex
align-items: center;
.sext-box
height: 76rpx;
width: 152rpx;
text-align: center;
line-height: 80rpx;
border-radius: 12rpx 12rpx 12rpx 12rpx
border: 2rpx solid #E8EAEE;
margin-left: 28rpx
font-weight: 400;
font-size: 28rpx;
.sext-boxactive
color: #256BFA
background: rgba(37,107,250,0.1);
border: 2rpx solid #256BFA;
.next-btn
width: 100%;
height: 90rpx;
background: #256BFA;
border-radius: 12rpx 12rpx 12rpx 12rpx;
font-weight: 500;
font-size: 32rpx;
color: #FFFFFF;
text-align: center;
line-height: 90rpx
// 技能信息样式
.content-skills
margin-bottom: 52rpx
.skills-header
display: flex
justify-content: space-between
align-items: center
margin-bottom: 32rpx
.input-titile
font-weight: 400
font-size: 28rpx
color: #6A6A6A
.add-skill-btn
padding: 16rpx 32rpx
background: #256BFA
color: #FFFFFF
border-radius: 8rpx
font-size: 26rpx
font-weight: 500
transition: all 0.3s ease
&:active
background: #1a5cd9
transform: scale(0.98)
&.disabled
background: #CCCCCC
color: #999999
cursor: not-allowed
&:active
background: #CCCCCC
transform: none
.skills-list
.skill-item
background: #FFFFFF
border: 2rpx solid #E8EAEE
border-radius: 12rpx
padding: 24rpx
margin-bottom: 24rpx
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05)
transition: all 0.3s ease
&:hover
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1)
border-color: #256BFA
.skill-header
display: flex
justify-content: space-between
align-items: center
margin-bottom: 20rpx
.skill-number
font-weight: 500
font-size: 28rpx
color: #333333
.skill-actions
.action-btn
padding: 8rpx 16rpx
border-radius: 6rpx
font-size: 24rpx
font-weight: 400
transition: all 0.2s ease
&.delete-btn
background: #FF4D4F
color: #FFFFFF
&:active
background: #D9363E
transform: scale(0.95)
.skill-fields
display: flex
flex-direction: column
gap: 20rpx
.skill-field
.field-label
font-weight: 400
font-size: 26rpx
color: #6A6A6A
margin-bottom: 8rpx
.field-input
font-weight: 400
font-size: 28rpx
color: #333333
line-height: 72rpx
height: 72rpx
border: 2rpx solid #E8EAEE
border-radius: 8rpx
padding: 0 20rpx
background: #F8F9FA
transition: all 0.3s ease
&:focus
border-color: #256BFA
background: #FFFFFF
&.triangle::before
right: 30rpx
top: calc(50% - 2rpx)
&.triangle::after
right: 30rpx
top: 50%
.empty-skills
text-align: center
padding: 60rpx 0
background: #F8F9FA
border-radius: 12rpx
border: 2rpx dashed #E8EAEE
.empty-text
font-size: 28rpx
color: #999999
font-weight: 400
</style>

View File

@@ -0,0 +1,441 @@
<template>
<div class="app-box">
<div class="con-box">
<!-- <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> -->
<scroll-view scroll-y class="main-scroll" @scrolltolower="handleScrollToLower">
<div class="cards" v-for="(item,index) in dataList" :key="item.examPaperId">
<div class="cardHead">
<div class="cardHeadLeft">
<div class="cardTitle">{{item.title}}</div>
</div>
<div class="rightBtn">
<uni-tag :inverted="true" v-if="item.status==2" text="已发布" type="success" />
<uni-tag :inverted="true" v-else-if="item.status==0" text="未发布" type="error" />
<uni-tag :inverted="true" v-else text="失效" />
</div>
</div>
<div class="heng"></div>
<div class="cardCon">
<div class="conten">发布时间{{item.publishTime}}</div>
<div class="conten">审核状态
<uni-tag :inverted="true" size="mini" v-if="item.shenhe==1" text="已通过" type="success" />
<uni-tag :inverted="true" size="mini" v-if="item.shenhe==2" text="未通过" type="error" />
<uni-tag :inverted="true" size="mini" v-if="item.shenhe==0" text="待审核" />
</div>
<div class="flooter">
<div v-if="item.status == '0' && item.shenhe == '1'" @click="handlePublishClick(scope.row,2)" >发布</div>
<div v-else @click="handlePublishClick(item,0)" >取消发布</div>
<div>查看</div>
<div v-if="item.status == '0'" @click="handleEditClick(item)">编辑</div>
<div v-if="item.status == '0'" @click="handleDeleteClick(item)">删除</div>
</div>
</div>
</div>
</scroll-view>
</div>
<div class="footer">
<div class="footerBtn" @click="handleOperation()">新增</div>
</div>
</div>
</template>
<script setup>
import { inject, ref, reactive } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
const { $api, navTo, navBack,urls } = inject('globalFunction');
import config from "@/config.js"
const userInfo = ref({});
const Authorization = ref('');
const searchKeyword = ref('');
const dataList=ref([])
const pageSize=ref(10)
const pageNum=ref(1)
const totalNum=ref(0)
const baseUrl = config.imgBaseUrl
const handleScrollToLower = () => {
getDataList('add');
};
onLoad(() => {
});
onShow(()=>{
Authorization.value=uni.getStorageSync('Padmin-Token')||''
getDataList('refresh');
})
// 搜索视频
function searchVideo() {
getDataList('refresh');
}
// 清除搜索内容
function clearSearch() {
searchKeyword.value = '';
getDataList('refresh');
}
function getDataList(type = 'add') {
let maxPage=Math.ceil(totalNum.value/pageSize.value)
let params={}
if (type === 'refresh') {
pageNum.value = 1;
params={
title:"",
pageSize:pageSize.value,
pageNum:pageNum.value,
type:2
}
$api.myRequest('/train/public/announcement/list', params).then((resData) => {
if(resData.code==200){
dataList.value=resData.rows
totalNum.value=resData.total
}
});
}
if (type === 'add' && pageNum.value < maxPage) {
pageNum.value += 1;
params={
address:"",
pageSize:pageSize.value,
pageNum:pageNum.value,
type:2
}
$api.myRequest('/train/public/announcement/list', params).then((resData) => {
if(resData.code==200){
dataList.value=dataList.value.concat(resData.rows)
totalNum.value=resData.total
}
});
}
}
function handlePublishClick(row,status){
if(status=='2'){
$api.myRequest('/train/public/announcement/publish', {id: row.id},'get',9100).then((resData) => {
if(resData.code==200){
$api.msg('发布成功');
getDataList('refresh');
}
});
} else {
$api.myRequest('/train/public/announcement/unpublish', {id: row.id},'get',9100).then((resData) => {
if(resData.code==200){
$api.msg('取消发布成功');
getDataList('refresh');
}
});
}
}
// 删除通知公告
function handleDeleteClick(row) {
uni.showModal({
title: '确认删除',
content: '确认删除该通知公告吗?',
success: (res) => {
if (res.confirm) {
$api.myRequest('/train/public/announcement/delete', {id: row.id},'get',9100).then((resData) => {
if(resData.code==200){
$api.msg('已删除');
getDataList('refresh');
}
});
}
}
});
}
function handleOperation(row) {
navTo(`/packageB/notice/evaluateAnnouncement/evaluateEdit`);
// navTo(`/packageB/institution/trainingInstitutionDetail?organId=${row.organId}`);
}
</script>
<style lang="stylus" scoped>
.app-box{
width: 100%;
height: 100vh;
position: relative;
.con-box{
position: absolute;
width: 100%;
height: 92%;
left: 0;
top:0;
z-index: 10;
padding: 20rpx 28rpx;
box-sizing: border-box;
overflow: hidden;
.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-scroll {
width: 100%;
height: 100%;
.cards{
width: 100%;
min-height: 260rpx;
height: auto;
background: linear-gradient(0deg, #E3EFFF 0%, #FBFDFF 100%);
// box-shadow: 0px 0px 6px 0px rgba(0,71,200,0.32);
border-radius: 12rpx;
border: 2px solid #EDF5FF;
margin-bottom: 30rpx;
padding: 30rpx 40rpx 0;
box-sizing: border-box
.cardHead{
display: flex;
align-items: center;
justify-content: space-between;
.cardHeadLeft{
display: flex;
align-items: center
width: 70%;
.cardTitle{
font-weight: bold;
font-size: 28rpx;
color: #0069CB;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.titleType{
border-radius: 4px;
font-size: 22rpx;
color: #157EFF;
width: 100rpx;
height: 38rpx;
text-align: center;
line-height: 38rpx;
margin-left: 10rpx;
}
}
}
.heng{
width: 30%;
height: 4rpx;
background: linear-gradient(88deg, #015EEA 0%, #00C0FA 100%);
margin: 10rpx 0 20rpx;
}
.cardCon{
display: flex;
flex-wrap: wrap;
.conten{
width: 100%;
font-size: 24rpx;
color: #666666;
display: flex;
align-items: center
margin-bottom: 10rpx;
}
.status-tags{
display: flex;
align-items: center;
}
}
.flooter{
width: 100%;
border-top: 1px solid #ccc;
display: flex;
justify-content: flex-end;
align-items: center;
margin-top: 10rpx;
view{
font-size: 28rpx;
margin-left: 30rpx;
color: #2175F3;
padding-top: 14rpx;
}
}
}
}
}
.cards2{
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100vh;
background-color: rgba(0,0,0,0.5);
z-index: 10000;
padding: 100rpx 50rpx;
box-sizing: border-box;
.cardCon{
height: 70%;
background-color: #fff;
padding: 20rpx;
box-sizing: border-box;
.cardHead{
display: flex;
align-items: center;
justify-content: space-between;
font-size: 30rpx;
font-weight: 600;
}
}
}
.footer{
width: 100%;
height: 120rpx;
background: #fff;
position: fixed;
z-index: 10;
bottom: 0;
left: 0;
display: flex;
align-content: center;
justify-content: center;
}
.footerBtn{
width: 90%;
height: 90rpx;
line-height: 90rpx;
text-align: center
border-radius: 10rpx;
color: #fff;
background: #409EFF;
}
}
.titleType{
display: inline-block
border-radius: 4px;
font-size: 22rpx;
color: #157EFF;
width: 100rpx;
height: 38rpx;
text-align: center;
line-height: 38rpx;
margin-left: 10rpx;
}
.primary{
border: 1px solid #157EFF!important;
color: #157EFF!important
}
.success{
border: 1px solid #05A636!important;
color: #05A636!important
}
.info{
border: 1px solid #898989!important;
color: #898989!important
}
.tertiary{
border: 1px solid #E6A340!important;
color: #E6A340!important
}
.primary2{
border: 1px solid #F56C6C!important;
color: #F56C6C!important
}
.rightBtn{
width: 140rpx;
height: 44rpx;
line-height: 44rpx;
// background: linear-gradient(90deg, #00C0FA 0%, #1271FF 100%);
// border-radius: 4px;
// color: #fff;
// font-size: 24rpx;
text-align: center;
}
.detailTitle{
font-size: 32rpx;
font-weight: 600;
margin: 30rpx 0;
}
.detailCon{
font-size: 28rpx;
line-height: 40rpx;
}
.exam-info {
display: flex;
justify-content: space-between;
margin-bottom: 35rpx;
margin-top: 20rpx;
}
.info-item {
flex: 1;
text-align: center;
}
.info-value {
font-family: 'D-DIN-Medium';
font-size: 26rpx;
font-weight: 600;
color: #409EFF;
margin-bottom: 8rpx;
}
.info-label {
font-size: 26rpx;
color: #333;
}
.info-divider {
width: 2px;
background-color: #C3E1FF;
}
</style>

View File

@@ -0,0 +1,469 @@
<template>
<div style="height: 90vh;overflow-y: auto;">
<view class="content">
<view class="content-input">
<view class="input-titile"><text class="input-required">*</text>标题</view>
<input class="input-con" v-model="fromValue.title" placeholder="请输入标题" />
</view>
<view class="content-input">
<view class="input-titile"><text class="input-required">*</text>联系人</view>
<!-- <input class="input-con" v-model="fromValue.contactName" placeholder="请输入联系人" /> -->
<unieditor @init-data="receiveInitData"/>
</view>
<view class="content-input">
<view class="input-titile">备注</view>
<textarea auto-height class="input-con" v-model="fromValue.remark" placeholder="请输入备注" />
</view>
</view>
<view class="footer">
<view class="footerBtn" @click="confirm()">保存</view>
</view>
<SelectPopup ref="selectPopupRef"></SelectPopup>
</div>
</template>
<script setup>
import { reactive, inject, watch, ref, onMounted, onUnmounted } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
const { $api, navTo, navBack, checkingPhoneRegExp,config } = inject('globalFunction');
import { storeToRefs } from 'pinia';
import useUserStore from '@/stores/useUserStore';
import useDictStore from '@/stores/useDictStore';
import SelectPopup from '@/components/selectPopup/selectPopup.vue';
import unieditor from '../../components/unieditor.vue'
const { userInfo } = storeToRefs(useUserStore());
const { getUserResume } = useUserStore();
const dictStore = useDictStore();
const { dictLabel, oneDictData, complete: dictComplete, getDictSelectOption } = dictStore;
let fromValue = reactive({
title: '',
content:'',
remark:'',
entProvince:'',
entCounty:'',
entCity:'',
status:'0',
});
let type=ref('add')
onLoad((options = {}) => {
});
// 监听页面显示,接收从技能查询页面返回的数据
onShow(() => {
getDetail()
// 通过事件总线接收技能选择结果
// uni.$on('skillSelected', handleSkillSelected);
});
// 页面卸载时移除事件监听
// onUnmounted(() => {
// uni.$off('skillSelected', handleSkillSelected);
// });
// 监听字典数据加载完成,自动更新学历显示
const receiveInitData = (initData) => {
fromValue.content=initData.value;
console.log('子组件初始化数据:', initData)
}
function getDetail(){
// if(userInfo.userId){
$api.myRequest("/train/public/train/organ/getTrainByUnifiedSocialCreditCode", {
tags: "913700001630477270",
}, "GET", 9100, {
Authorization: `Bearer ${uni.getStorageSync('token')}`,
'Content-Type': "application/x-www-form-urlencoded"
}).then((res) => {
if (res.code === 200) {
fromValue.entProvince = res.data.organProvince;
fromValue.entCounty = res.data.organCounty;
fromValue.entCity = res.data.organCity;
}
})
// }else{
// $api.msg('请先登录');
// }
}
const confirm = () => {
if (!fromValue.title) {
return $api.msg('请输入标题');
}
if (!fromValue.content) {
return $api.msg('请输入内容');
}
const params = {
...fromValue,
};
if(type.value=='add'){
$api.myRequest('/train/public/announcement/add', {
...fromValue,
type:1
}, 'post', 9100, {
Authorization: `Bearer ${uni.getStorageSync('token')}`,
'Content-Type': "application/x-www-form-urlencoded"
}).then((resData) => {
if(resData.code==200){
$api.msg('新增成功');
setTimeout(()=>{
navBack();
},2000)
}
});
} else if (type.value=='edit'){
$api.myRequest('/train/public/announcement/update', {
...fromValue,
type:1
}, 'post', 9100, {
Authorization: `Bearer ${uni.getStorageSync('token')}`,
'Content-Type': "application/x-www-form-urlencoded"
}).then((resData) => {
if(resData.code==200){
$api.msg('更新成功');
setTimeout(()=>{
navBack();
},2000)
}
});
}
};
</script>
<style lang="stylus" scoped>
.btn{
margin-top: -30rpx
}
.input-required{
color: red
}
/* 上传按钮 */
.upload-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 10rpx;
padding: 0 30rpx;
background-color: #f5f7fa;
color: #1677ff;
border-radius: 8rpx;
font-size: 26rpx;
margin-top: 20rpx;
}
.upload-icon {
width: 30rpx;
height: 30rpx;
}
.reupload-text {
font-size: 22rpx;
color: #666;
}
/* 上传说明文字 */
.upload-tip {
font-size: 20rpx;
color: #999;
text-align: center;
line-height: 1.4;
}
/* 已上传文件信息 */
.uploaded-file-info>view {
display: flex;
align-items: center;
gap: 15rpx;
padding: 15rpx 20rpx;
background-color: #fafafa;
border-radius: 8rpx;
margin-top: 10rpx;
}
.file-icon {
width: 36rpx;
height: 36rpx;
}
.file-name {
font-size: 24rpx;
color: #333;
max-width: 400rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.delete-file-btn {
padding: 0 15rpx;
height: 36rpx;
line-height: 36rpx;
font-size: 22rpx;
color: #ff4d4f;
background-color: transparent;
}
.content{
padding: 28rpx;
display: flex;
flex-direction: column;
justify-content: flex-start
height: calc(100% - 120rpx)
}
.footer{
width: 100%;
height: 120rpx;
background: #fff;
position: fixed;
z-index: 10;
bottom: 0;
left: 0;
display: flex;
align-content: center;
justify-content: center;
}
.footerBtn{
width: 90%;
height: 90rpx;
line-height: 90rpx;
text-align: center
border-radius: 10rpx;
color: #fff;
background: #409EFF;
}
.content-input
margin-bottom: 52rpx
.input-titile
font-weight: 400;
font-size: 28rpx;
color: #6A6A6A
.input-con
font-weight: 400;
font-size: 32rpx;
color: #333333;
line-height: 80rpx;
height: 80rpx;
border-bottom: 2rpx solid #EBEBEB
position: relative;
.error-message
color: #ff4757;
font-size: 24rpx;
margin-top: 10rpx;
line-height: 1.4;
.success-message
color: #2ed573;
font-size: 24rpx;
margin-top: 10rpx;
line-height: 1.4;
.input-error
.input-con
border-bottom-color: #ff4757;
.triangle::before
position: absolute;
right: 20rpx;
top: calc(50% - 2rpx);
content: '';
width: 4rpx;
height: 18rpx;
border-radius: 2rpx
background: #697279;
transform: translate(0, -50%) rotate(-45deg) ;
.triangle::after
position: absolute;
right: 20rpx;
top: 50%;
content: '';
width: 4rpx;
height: 18rpx;
border-radius: 2rpx
background: #697279;
transform: rotate(45deg)
.input-nx
position: relative
border-bottom: 2rpx solid #EBEBEB
padding-bottom: 30rpx
display: flex
flex-wrap: wrap
.nx-item
padding: 16rpx 24rpx
width: fit-content
border-radius: 20rpx
border: 2rpx solid #E8EAEE
background-color: #f8f9fa
margin-right: 16rpx
margin-top: 16rpx
font-size: 28rpx
color: #333333
transition: all 0.2s ease
&:hover
background-color: #e9ecef
border-color: #256bfa
color: #256bfa
.content-sex
height: 110rpx;
display: flex
justify-content: space-between;
align-items: flex-start;
border-bottom: 2rpx solid #EBEBEB
margin-bottom: 52rpx
.sex-titile
line-height: 80rpx;
.sext-ri
display: flex
align-items: center;
.sext-box
height: 76rpx;
width: 152rpx;
text-align: center;
line-height: 80rpx;
border-radius: 12rpx 12rpx 12rpx 12rpx
border: 2rpx solid #E8EAEE;
margin-left: 28rpx
font-weight: 400;
font-size: 28rpx;
.sext-boxactive
color: #256BFA
background: rgba(37,107,250,0.1);
border: 2rpx solid #256BFA;
.next-btn
width: 100%;
height: 90rpx;
background: #256BFA;
border-radius: 12rpx 12rpx 12rpx 12rpx;
font-weight: 500;
font-size: 32rpx;
color: #FFFFFF;
text-align: center;
line-height: 90rpx
// 技能信息样式
.content-skills
margin-bottom: 52rpx
.skills-header
display: flex
justify-content: space-between
align-items: center
margin-bottom: 32rpx
.input-titile
font-weight: 400
font-size: 28rpx
color: #6A6A6A
.add-skill-btn
padding: 16rpx 32rpx
background: #256BFA
color: #FFFFFF
border-radius: 8rpx
font-size: 26rpx
font-weight: 500
transition: all 0.3s ease
&:active
background: #1a5cd9
transform: scale(0.98)
&.disabled
background: #CCCCCC
color: #999999
cursor: not-allowed
&:active
background: #CCCCCC
transform: none
.skills-list
.skill-item
background: #FFFFFF
border: 2rpx solid #E8EAEE
border-radius: 12rpx
padding: 24rpx
margin-bottom: 24rpx
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05)
transition: all 0.3s ease
&:hover
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1)
border-color: #256BFA
.skill-header
display: flex
justify-content: space-between
align-items: center
margin-bottom: 20rpx
.skill-number
font-weight: 500
font-size: 28rpx
color: #333333
.skill-actions
.action-btn
padding: 8rpx 16rpx
border-radius: 6rpx
font-size: 24rpx
font-weight: 400
transition: all 0.2s ease
&.delete-btn
background: #FF4D4F
color: #FFFFFF
&:active
background: #D9363E
transform: scale(0.95)
.skill-fields
display: flex
flex-direction: column
gap: 20rpx
.skill-field
.field-label
font-weight: 400
font-size: 26rpx
color: #6A6A6A
margin-bottom: 8rpx
.field-input
font-weight: 400
font-size: 28rpx
color: #333333
line-height: 72rpx
height: 72rpx
border: 2rpx solid #E8EAEE
border-radius: 8rpx
padding: 0 20rpx
background: #F8F9FA
transition: all 0.3s ease
&:focus
border-color: #256BFA
background: #FFFFFF
&.triangle::before
right: 30rpx
top: calc(50% - 2rpx)
&.triangle::after
right: 30rpx
top: 50%
.empty-skills
text-align: center
padding: 60rpx 0
background: #F8F9FA
border-radius: 12rpx
border: 2rpx dashed #E8EAEE
.empty-text
font-size: 28rpx
color: #999999
font-weight: 400
</style>

View File

@@ -0,0 +1,441 @@
<template>
<div class="app-box">
<div class="con-box">
<!-- <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> -->
<scroll-view scroll-y class="main-scroll" @scrolltolower="handleScrollToLower">
<div class="cards" v-for="(item,index) in dataList" :key="item.examPaperId">
<div class="cardHead">
<div class="cardHeadLeft">
<div class="cardTitle">{{item.title}}</div>
</div>
<div class="rightBtn">
<uni-tag :inverted="true" v-if="item.status==2" text="已发布" type="success" />
<uni-tag :inverted="true" v-else-if="item.status==0" text="未发布" type="error" />
<uni-tag :inverted="true" v-else text="失效" />
</div>
</div>
<div class="heng"></div>
<div class="cardCon">
<div class="conten">发布时间{{item.publishTime}}</div>
<div class="conten">审核状态
<uni-tag :inverted="true" size="mini" v-if="item.shenhe==1" text="已通过" type="success" />
<uni-tag :inverted="true" size="mini" v-if="item.shenhe==2" text="未通过" type="error" />
<uni-tag :inverted="true" size="mini" v-if="item.shenhe==0" text="待审核" />
</div>
<div class="flooter">
<div v-if="item.status == '0' && item.shenhe == '1'" @click="handlePublishClick(scope.row,2)" >发布</div>
<div v-else @click="handlePublishClick(item,0)" >取消发布</div>
<div>查看</div>
<div v-if="item.status == '0'" @click="handleEditClick(item)">编辑</div>
<div v-if="item.status == '0'" @click="handleDeleteClick(item)">删除</div>
</div>
</div>
</div>
</scroll-view>
</div>
<div class="footer">
<div class="footerBtn" @click="handleOperation()">新增</div>
</div>
</div>
</template>
<script setup>
import { inject, ref, reactive } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
const { $api, navTo, navBack,urls } = inject('globalFunction');
import config from "@/config.js"
const userInfo = ref({});
const Authorization = ref('');
const searchKeyword = ref('');
const dataList=ref([])
const pageSize=ref(10)
const pageNum=ref(1)
const totalNum=ref(0)
const baseUrl = config.imgBaseUrl
const handleScrollToLower = () => {
getDataList('add');
};
onLoad(() => {
});
onShow(()=>{
Authorization.value=uni.getStorageSync('Padmin-Token')||''
getDataList('refresh');
})
// 搜索视频
function searchVideo() {
getDataList('refresh');
}
// 清除搜索内容
function clearSearch() {
searchKeyword.value = '';
getDataList('refresh');
}
function getDataList(type = 'add') {
let maxPage=Math.ceil(totalNum.value/pageSize.value)
let params={}
if (type === 'refresh') {
pageNum.value = 1;
params={
title:"",
pageSize:pageSize.value,
pageNum:pageNum.value,
type:1
}
$api.myRequest('/train/public/announcement/list', params).then((resData) => {
if(resData.code==200){
dataList.value=resData.rows
totalNum.value=resData.total
}
});
}
if (type === 'add' && pageNum.value < maxPage) {
pageNum.value += 1;
params={
address:"",
pageSize:pageSize.value,
pageNum:pageNum.value,
type:1
}
$api.myRequest('/train/public/train/organ/table', params).then((resData) => {
if(resData.code==200){
dataList.value=dataList.value.concat(resData.rows)
totalNum.value=resData.total
}
});
}
}
function handlePublishClick(row,status){
if(status=='2'){
$api.myRequest('/train/public/announcement/publish', {id: row.id},'get',9100).then((resData) => {
if(resData.code==200){
$api.msg('发布成功');
getDataList('refresh');
}
});
} else {
$api.myRequest('/train/public/announcement/unpublish', {id: row.id},'get',9100).then((resData) => {
if(resData.code==200){
$api.msg('取消发布成功');
getDataList('refresh');
}
});
}
}
// 删除通知公告
function handleDeleteClick(row) {
uni.showModal({
title: '确认删除',
content: '确认删除该通知公告吗?',
success: (res) => {
if (res.confirm) {
$api.myRequest('/train/public/announcement/delete', {id: row.id},'get',9100).then((resData) => {
if(resData.code==200){
$api.msg('已删除');
getDataList('refresh');
}
});
}
}
});
}
function handleOperation(row) {
navTo(`/packageB/notice/trainingAnnouncement/postedEdit`);
// navTo(`/packageB/institution/trainingInstitutionDetail?organId=${row.organId}`);
}
</script>
<style lang="stylus" scoped>
.app-box{
width: 100%;
height: 100vh;
position: relative;
.con-box{
position: absolute;
width: 100%;
height: 92%;
left: 0;
top:0;
z-index: 10;
padding: 20rpx 28rpx;
box-sizing: border-box;
overflow: hidden;
.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-scroll {
width: 100%;
height: 100%;
.cards{
width: 100%;
min-height: 260rpx;
height: auto;
background: linear-gradient(0deg, #E3EFFF 0%, #FBFDFF 100%);
// box-shadow: 0px 0px 6px 0px rgba(0,71,200,0.32);
border-radius: 12rpx;
border: 2px solid #EDF5FF;
margin-bottom: 30rpx;
padding: 30rpx 40rpx 0;
box-sizing: border-box
.cardHead{
display: flex;
align-items: center;
justify-content: space-between;
.cardHeadLeft{
display: flex;
align-items: center
width: 70%;
.cardTitle{
font-weight: bold;
font-size: 28rpx;
color: #0069CB;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.titleType{
border-radius: 4px;
font-size: 22rpx;
color: #157EFF;
width: 100rpx;
height: 38rpx;
text-align: center;
line-height: 38rpx;
margin-left: 10rpx;
}
}
}
.heng{
width: 30%;
height: 4rpx;
background: linear-gradient(88deg, #015EEA 0%, #00C0FA 100%);
margin: 10rpx 0 20rpx;
}
.cardCon{
display: flex;
flex-wrap: wrap;
.conten{
width: 100%;
font-size: 24rpx;
color: #666666;
display: flex;
align-items: center
margin-bottom: 10rpx;
}
.status-tags{
display: flex;
align-items: center;
}
}
.flooter{
width: 100%;
border-top: 1px solid #ccc;
display: flex;
justify-content: flex-end;
align-items: center;
margin-top: 10rpx;
view{
font-size: 28rpx;
margin-left: 30rpx;
color: #2175F3;
padding-top: 14rpx;
}
}
}
}
}
.cards2{
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100vh;
background-color: rgba(0,0,0,0.5);
z-index: 10000;
padding: 100rpx 50rpx;
box-sizing: border-box;
.cardCon{
height: 70%;
background-color: #fff;
padding: 20rpx;
box-sizing: border-box;
.cardHead{
display: flex;
align-items: center;
justify-content: space-between;
font-size: 30rpx;
font-weight: 600;
}
}
}
.footer{
width: 100%;
height: 120rpx;
background: #fff;
position: fixed;
z-index: 10;
bottom: 0;
left: 0;
display: flex;
align-content: center;
justify-content: center;
}
.footerBtn{
width: 90%;
height: 90rpx;
line-height: 90rpx;
text-align: center
border-radius: 10rpx;
color: #fff;
background: #409EFF;
}
}
.titleType{
display: inline-block
border-radius: 4px;
font-size: 22rpx;
color: #157EFF;
width: 100rpx;
height: 38rpx;
text-align: center;
line-height: 38rpx;
margin-left: 10rpx;
}
.primary{
border: 1px solid #157EFF!important;
color: #157EFF!important
}
.success{
border: 1px solid #05A636!important;
color: #05A636!important
}
.info{
border: 1px solid #898989!important;
color: #898989!important
}
.tertiary{
border: 1px solid #E6A340!important;
color: #E6A340!important
}
.primary2{
border: 1px solid #F56C6C!important;
color: #F56C6C!important
}
.rightBtn{
width: 140rpx;
height: 44rpx;
line-height: 44rpx;
// background: linear-gradient(90deg, #00C0FA 0%, #1271FF 100%);
// border-radius: 4px;
// color: #fff;
// font-size: 24rpx;
text-align: center;
}
.detailTitle{
font-size: 32rpx;
font-weight: 600;
margin: 30rpx 0;
}
.detailCon{
font-size: 28rpx;
line-height: 40rpx;
}
.exam-info {
display: flex;
justify-content: space-between;
margin-bottom: 35rpx;
margin-top: 20rpx;
}
.info-item {
flex: 1;
text-align: center;
}
.info-value {
font-family: 'D-DIN-Medium';
font-size: 26rpx;
font-weight: 600;
color: #409EFF;
margin-bottom: 8rpx;
}
.info-label {
font-size: 26rpx;
color: #333;
}
.info-divider {
width: 2px;
background-color: #C3E1FF;
}
</style>

View File

@@ -474,6 +474,48 @@
"style": {
"navigationBarTitleText": "培训机构信息详情"
}
},
{
"path": "institution/trainingInstitutionMaintenance",
"style": {
"navigationBarTitleText": "培训机构信息维护"
}
},
{
"path": "components/map",
"style": {
"navigationBarTitleText": "选择地址"
}
},
{
"path": "institution/evaluationAgencyMaintenance",
"style": {
"navigationBarTitleText": "评价机构信息维护"
}
},
{
"path": "notice/trainingAnnouncement/postedList",
"style": {
"navigationBarTitleText": "培训公告发布"
}
},
{
"path": "notice/trainingAnnouncement/postedEdit",
"style": {
"navigationBarTitleText": "培训公告"
}
},
{
"path": "notice/evaluateAnnouncement/evaluateList",
"style": {
"navigationBarTitleText": "评价公告发布"
}
},
{
"path": "notice/evaluateAnnouncement/evaluateEdit",
"style": {
"navigationBarTitleText": "评价公告"
}
}
]
},