Browse Source

修改 bug

master
王泽彦 8 months ago
parent
commit
4ad6b5e0c8
  1. 2
      admin/src/app/api/salary.ts
  2. 2
      admin/src/app/views/salary/components/salary-edit.vue
  3. 25
      admin/src/app/views/salary/list.vue
  4. 8
      admin/src/components/direct-upload/index.vue
  5. 94
      admin/src/utils/directUpload.ts
  6. 3
      niucloud/app/adminapi/controller/lesson_course_teaching/LessonCourseTeaching.php
  7. 2
      niucloud/app/adminapi/route/sys.php
  8. 6
      niucloud/app/service/admin/lesson_course_teaching/LessonCourseTeachingService.php
  9. 28
      niucloud/app/service/admin/upload/DirectUploadService.php

2
admin/src/app/api/salary.ts

@ -123,7 +123,7 @@ export const getStatisticsTrend = (params: { months?: number; campus_id?: number
// 获取员工列表
export const getPersonnelList = () => {
return request.get('/personnel/list')
return request.get('/lesson_course_teaching/personnel_data_all',{params:{is_all:1}})
}
// 获取校区列表

2
admin/src/app/views/salary/components/salary-edit.vue

@ -45,7 +45,7 @@
<el-option
v-for="item in campusList"
:key="item.id"
:label="item.name"
:label="item.campus_name"
:value="item.id"
/>
</el-select>

25
admin/src/app/views/salary/list.vue

@ -29,7 +29,7 @@
<el-option
v-for="item in campusList"
:key="item.id"
:label="item.name"
:label="item.campus_name"
:value="item.id"
/>
</el-select>
@ -223,6 +223,7 @@ import {
type QueryParams,
type SalaryItem
} from '@/app/api/salary'
import SalaryEdit from './components/salary-edit.vue'
import SalaryDetail from './components/salary-detail.vue'
@ -275,7 +276,27 @@ const loadTableData = async () => {
loading.value = true
try {
const res = await getSalaryList(queryParams)
tableData.value = res.data.list
// 访
const rawData = res.data.data || []
//
tableData.value = rawData.map((item: any) => ({
...item,
base_salary: Number(item.base_salary) || 0,
work_salary: Number(item.work_salary) || 0,
mgr_performance: Number(item.mgr_performance) || 0,
performance_bonus: Number(item.performance_bonus) || 0,
other_subsidies: Number(item.other_subsidies) || 0,
deductions: Number(item.deductions) || 0,
gross_salary: Number(item.gross_salary) || 0,
social_security: Number(item.social_security) || 0,
individual_income_tax: Number(item.individual_income_tax) || 0,
net_salary: Number(item.net_salary) || 0,
attendance: Number(item.attendance) || 0,
full_attendance_days: Number(item.full_attendance_days) || 0
}))
total.value = res.data.total
} catch (error) {
console.error('加载数据失败:', error)

8
admin/src/components/direct-upload/index.vue

@ -139,7 +139,7 @@ const handleFileChange = async (uploadFile: UploadFile) => {
/**
* 直传上传
*/
const handleDirectUpload = async (file: File, uploadFile: UploadFile) => {
const handleDirectUpload = async (file: File, uploadFileObj: UploadFile) => {
const result = await uploadFile({
file,
fileType: props.fileType,
@ -151,7 +151,7 @@ const handleDirectUpload = async (file: File, uploadFile: UploadFile) => {
if (result.success) {
ElMessage.success('上传成功')
emit('success', result.url, uploadFile)
emit('success', result.url, uploadFileObj)
} else {
throw new Error(result.error || '上传失败')
}
@ -160,7 +160,7 @@ const handleDirectUpload = async (file: File, uploadFile: UploadFile) => {
/**
* 后备上传传统方式
*/
const handleFallbackUpload = async (file: File, uploadFile: UploadFile) => {
const handleFallbackUpload = async (file: File, uploadFileObj: UploadFile) => {
if (!props.fallbackUrl) {
throw new Error('直传不可用且未配置后备上传接口')
}
@ -186,7 +186,7 @@ const handleFallbackUpload = async (file: File, uploadFile: UploadFile) => {
const response = JSON.parse(xhr.responseText)
if (response.code === 1) {
ElMessage.success('上传成功')
emit('success', response.data.url, uploadFile)
emit('success', response.data.url, uploadFileObj)
resolve()
} else {
reject(new Error(response.msg || '上传失败'))

94
admin/src/utils/directUpload.ts

@ -119,13 +119,38 @@ export class DirectUpload {
file: File,
credentials: UploadCredentials,
onProgress?: (percent: number) => void
): Promise<UploadResult> {
return new Promise(async (resolve, reject) => {
const filePath = credentials.file_path.replace('${filename}', file.name)
// 尝试直传,如果CORS失败则使用代理
try {
const directResult = await this.directUploadToTencent(file, credentials, filePath, onProgress)
resolve(directResult)
} catch (error) {
console.warn('Direct upload failed, trying proxy:', error)
try {
const proxyResult = await this.proxyUploadToTencent(file, credentials, filePath, onProgress)
resolve(proxyResult)
} catch (proxyError) {
reject(proxyError)
}
}
})
}
/**
* COS直接上传
*/
private async directUploadToTencent(
file: File,
credentials: UploadCredentials,
filePath: string,
onProgress?: (percent: number) => void
): Promise<UploadResult> {
return new Promise((resolve, reject) => {
const formData = new FormData()
// 生成文件路径(替换模板中的${filename})
const filePath = credentials.file_path.replace('${filename}', file.name)
// 添加腾讯云COS必需的字段
formData.append('key', filePath)
formData.append('policy', credentials.credentials.policy)
@ -171,6 +196,69 @@ export class DirectUpload {
})
}
/**
* COS代理上传
*/
private async proxyUploadToTencent(
file: File,
credentials: UploadCredentials,
filePath: string,
onProgress?: (percent: number) => void
): Promise<UploadResult> {
return new Promise((resolve, reject) => {
const formData = new FormData()
// 添加后端代理需要的参数(分别发送各个字段,避免JSON解析问题)
formData.append('upload_url', credentials.upload_url)
formData.append('key', filePath)
formData.append('policy', credentials.credentials.policy)
formData.append('q-sign-algorithm', credentials.credentials['q-sign-algorithm'])
formData.append('q-ak', credentials.credentials['q-ak'])
formData.append('q-key-time', credentials.credentials['q-key-time'])
formData.append('q-signature', credentials.credentials['q-signature'])
formData.append('domain', credentials.domain)
formData.append('file', file)
const xhr = new XMLHttpRequest()
// 上传进度
if (onProgress) {
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable) {
const percent = Math.round((event.loaded / event.total) * 100)
onProgress(percent)
}
})
}
// 上传完成
xhr.addEventListener('load', () => {
try {
const response = JSON.parse(xhr.responseText)
if (response.code === 1) {
resolve({
success: true,
url: response.data.url
})
} else {
reject(new Error(response.msg || '上传失败'))
}
} catch (error) {
reject(new Error('响应解析失败'))
}
})
// 上传错误
xhr.addEventListener('error', () => {
reject(new Error('网络错误,上传失败'))
})
// 执行代理上传
xhr.open('POST', `${this.baseUrl}/adminapi/sys/direct/cos_proxy`, true)
xhr.send(formData)
})
}
/**
* OSS直传
*/

3
niucloud/app/adminapi/controller/lesson_course_teaching/LessonCourseTeaching.php

@ -756,7 +756,8 @@ class LessonCourseTeaching extends BaseAdminController
["name",""],
["phone",""],
["role_id",""],
["dept_id",""]
["dept_id",""],
['is_all',""]
]);
return success((new LessonCourseTeachingService())->getPersonnelDataAll($data));
}

2
niucloud/app/adminapi/route/sys.php

@ -333,6 +333,8 @@ Route::group('sys', function() {
Route::get('direct/credentials', 'upload.DirectUpload/getUploadCredentials');
//确认上传完成
Route::post('direct/confirm', 'upload.DirectUpload/confirmUpload');
//COS代理上传(开发环境临时方案)
Route::post('direct/cos_proxy', 'upload.CosProxy/proxyUpload');
//附件列表
Route::get('attachment', 'sys.Attachment/lists');

6
niucloud/app/service/admin/lesson_course_teaching/LessonCourseTeachingService.php

@ -734,8 +734,12 @@ class LessonCourseTeachingService extends BaseAdminService
->where($whereArr)
->group("a.id")
->order($order);
if ($where['is_all']){
$list = $search_model->select()->toArray();
}else{
$list = $this->pageQuery($search_model);
}
return $list;
}

28
niucloud/app/service/admin/upload/DirectUploadService.php

@ -85,13 +85,20 @@ class DirectUploadService extends BaseAdminService
}
}
// 生成策略
$expired = time() + 3600; // 1小时过期
$current_time = time();
$expired = $current_time + 3600; // 1小时过期
$key_time = $current_time . ';' . $expired;
// 生成POST策略 (腾讯云COS对象格式)
$policy = [
'expiration' => gmdate('Y-m-d\TH:i:s.000\Z', $expired),
'conditions' => [
['bucket' => $config['bucket']],
['starts-with' => '$key', $file_path]
// key路径限制 - starts-with格式
['starts-with', '$key', dirname($file_path) . '/'],
// 签名字段 - 对象格式 (腾讯云COS要求)
['$q-sign-algorithm' => 'sha1'],
['$q-ak' => $config['access_key']],
['$q-key-time' => $key_time]
]
];
@ -101,9 +108,12 @@ class DirectUploadService extends BaseAdminService
$policy['conditions'][] = ['content-length-range', 0, $max_size];
}
$policy_encoded = base64_encode(json_encode($policy));
$signature = hash_hmac('sha1', $policy_encoded, $config['secret_key'], true);
$signature_encoded = base64_encode($signature);
$policy_encoded = base64_encode(json_encode($policy, JSON_UNESCAPED_SLASHES));
// 腾讯云COS POST签名算法
$sign_key = hash_hmac('sha1', $key_time, $config['secret_key']);
$string_to_sign = $policy_encoded;
$signature = hash_hmac('sha1', $string_to_sign, $sign_key);
return [
'storage_type' => 'tencent',
@ -112,8 +122,8 @@ class DirectUploadService extends BaseAdminService
'policy' => $policy_encoded,
'q-sign-algorithm' => 'sha1',
'q-ak' => $config['access_key'],
'q-key-time' => time() . ';' . $expired,
'q-signature' => $signature_encoded,
'q-key-time' => $key_time,
'q-signature' => $signature,
],
'file_path' => $file_path,
'domain' => $config['domain'] ?? "https://{$config['bucket']}.cos.{$config['region']}.myqcloud.com",

Loading…
Cancel
Save