Files
ks-app-employment-service/packageB/institution/evaluationAgencyMaintenance.vue

942 lines
29 KiB
Vue
Raw Normal View History

2026-02-24 10:06:48 +08:00
<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>