<template>
	<view class="page">
		<scroll-view class="scroll" scroll-y>
			<view class="card" v-for="section in sections" :key="section.key">
				<view class="card-title">{{ section.title }}</view>
				<view v-for="field in section.fields" :key="field.key" class="field">
					<text class="label">{{ field.label }}<text v-if="field.required" class="req">*</text></text>
					<template v-if="field.type === 'select'"><picker mode="selector" :range="field.options" @change="e => onSelect(field.key, field.options, e)"><view class="input-like clickable">{{ form[field.key] || field.placeholder }}</view></picker></template>
					<template v-else-if="field.type === 'datetime'"><view class="input-like clickable" @tap="openDateTime(field.key)">{{ formatDateTime(field.key) || field.placeholder }}</view></template>
					<template v-else-if="field.type === 'city'"><view class="input-like clickable" @tap="openCity(field.key)">{{ form[field.key] || field.placeholder }}</view></template>
					<template v-else-if="field.type === 'computed'"><view class="computed-box">{{ computedFieldText(field.key) || field.placeholder || '—' }}</view></template>
					<template v-else-if="field.type === 'file'">
						<view class="file-picker-wrap">
							<view class="upload-box clickable" @tap="openFilePicker(field.key)">
								<view class="upload-hint">{{ field.placeholder || '点击上传文件' }}</view>
								<view class="upload-sub">支持文件上传，点击后选择文件</view>
							</view>
							<view class="file-list" v-if="fileFields[field.key] && fileFields[field.key].length">
								<view class="file-item" v-for="(file, idx) in fileFields[field.key]" :key="file.ossId || file.url || idx">
									<text class="file-name">{{ file.originalName || file.fileName || file.url }}</text>
									<text class="remove" @tap="removeFile(field.key, idx)">删除</text>
								</view>
							</view>
						</view>
					</template>
					<template v-else-if="field.type === 'textarea'"><textarea class="textarea" v-model="form[field.key]" :maxlength="field.maxlength || 200" :placeholder="field.placeholder" /></template>
					<template v-else><input class="input" :type="field.inputType || 'text'" v-model="form[field.key]" :placeholder="field.placeholder" /></template>
				</view>
			</view>
			<view class="card"><view class="card-title">审批方式</view><view class="field"><text class="label">审批人<text class="req">*</text></text><view class="input-like clickable" @tap="openEmployeePicker('assignee')">{{ assigneeLabel || '点击选择审批人' }}</view></view></view>
			<view class="card"><view class="card-title">抄送</view><view class="field"><text class="label">抄送人</text><view class="input-like clickable" @tap="openEmployeePicker('cc')">{{ ccUsers.length ? `已选择 ${ccUsers.length} 人` : '点击选择抄送人' }}</view></view></view>
			<view class="card"><view class="card-title">提交</view><button class="btn primary" :loading="submitting" @tap="submit">提交申请</button></view>
		</scroll-view>
		<x-file-picker ref="globalFilePicker" @select="onGlobalFileSelect" />
		<employee-picker :visible.sync="employeeSheetVisible" :title="employeeMode === 'assignee' ? '选择审批人' : '选择抄送人'" :list="employees" :multiple="employeeMode === 'cc'" :value="employeeMode === 'assignee' ? assignee : ccUsers" @change="onEmployeeChange" />
		<date-time-picker :visible.sync="dateTimeSheetVisible" :value="form[datetimeFieldKey]" @change="onDateTimeChange" />
		<city-picker :visible.sync="citySheetVisible" :value="form[cityFieldKey]" @change="onCityChange" />
	</view>
</template>

<script>
import { ccFlowTask } from '@/api/hrm/flow'
import { listUser } from '@/api/oa/user'
import { getEmployeeByUserId } from '@/api/hrm/employee'
import { uploadFile } from '@/api/common/upload'
import EmployeePicker from '@/components/hrm/EmployeePicker.vue'
import DateTimePicker from '@/components/hrm/DateTimePicker.vue'
import CityPicker from '@/components/hrm/CityPicker.vue'
import XFilePicker from '@/components/x-native-uploader/x-file-picker.vue'

export default {
	components: { EmployeePicker, DateTimePicker, CityPicker, XFilePicker },
	props: { title: String, subtitle: String, bizType: String, requestApi: Function, initialForm: Object, sections: Array, flowFields: { type: Array, default: () => [] } },
	data() { return { submitting: false, form: JSON.parse(JSON.stringify(this.initialForm || {})), employees: [], employeeSheetVisible: false, employeeMode: 'assignee', assignee: null, ccUsers: [], cachedAssigneeKey: '', dateTimeSheetVisible: false, datetimeFieldKey: '', citySheetVisible: false, cityFieldKey: '', imageFields: {}, fileFields: {}, currentEmpId: null } },
	computed: { assigneeLabel() { return this.assignee ? this.employeeName(this.assignee) : '' } },
	async created() { 
		this.cachedAssigneeKey = `hrm_manual_assignee_${this.bizType || 'default'}`; 
		await this.loadEmployees(); 
		await this.loadCurrentEmpId()
		this.restoreAssignee() 
	},
	watch: { 
		'form.startTime'(val) { this.autoFillHours() }, 
		'form.endTime'(val) { this.autoFillHours() } 
	},
	methods: {
		onSelect(fieldKey, options, e) {
			const idx = Number(e.detail.value)
			this.$set(this.form, fieldKey, options[idx])
		},
		employeeName(emp) { return emp?.empName || emp?.nickName || emp?.userName || emp?.realName || `员工${emp?.userId || emp?.id || ''}` },
		employeeDept(emp) { return emp?.dept?.deptName || emp?.deptName || '未分配部门' },
		async loadEmployees() { try { const res = await listUser({ pageNum: 1, pageSize: 500 }); this.employees = res.rows || res.data || [] } catch (e) { this.employees = [] } },
		async loadCurrentEmpId() { try { const oaId = uni.getStorageSync('oaId'); if (!oaId) return; const emp = await getEmployeeByUserId(oaId); if (emp?.data?.empId) this.currentEmpId = emp.data.empId } catch (e) { console.log('[loadCurrentEmpId] error', e) } },
		restoreAssignee() { try { const raw = uni.getStorageSync(this.cachedAssigneeKey); if (!raw) return; const cached = typeof raw === 'string' ? JSON.parse(raw) : raw; const id = String(cached.userId || cached.empId || ''); const hit = this.employees.find(e => String(e.userId || e.id) === id); if (hit) this.assignee = hit } catch (e) {} },
		openEmployeePicker(mode) { this.employeeMode = mode; this.employeeSheetVisible = true },
		onEmployeeChange(val) { if (this.employeeMode === 'assignee') { this.assignee = val; try { uni.setStorageSync(this.cachedAssigneeKey, JSON.stringify({ userId: val.userId || val.id, empId: val.empId || '', empName: this.employeeName(val), deptName: this.employeeDept(val) })) } catch (e) {} } else { this.ccUsers = val || [] } },
		openFilePicker(fieldKey) {
			this.currentFileFieldKey = fieldKey
			console.log('[RequestForm] openFilePicker', fieldKey, this.$refs.globalFilePicker)
			const picker = this.$refs.globalFilePicker
			if (picker && picker.open) {
				picker.open()
			} else {
				console.warn('[RequestForm] file picker ref missing or open() unavailable,', fieldKey, picker)
			}
		},
		async onGlobalFileSelect(payload) {
			const fieldKey = this.currentFileFieldKey
			console.log('[RequestForm] file select', fieldKey, payload)
			if (!fieldKey) return
			const files = Array.isArray(payload) ? payload : [payload]
			if (!this.fileFields[fieldKey]) this.$set(this.fileFields, fieldKey, [])
			for (const f of files.filter(Boolean)) {
				try {
					const uploaded = await uploadFile({
						path: f.path || f.url || '',
						name: f.name || f.fileName || 'file',
						size: f.size || 0,
						type: f.type || ''
					})
					this.fileFields[fieldKey].push({
						name: uploaded.fileName || f.name || f.fileName || 'file',
						extname: (f.name || f.fileName || '').split('.').pop() || '',
						url: uploaded.url || f.path || f.url || '',
						ossId: uploaded.ossId || uploaded.url || f.path || f.url || '',
						fileName: uploaded.fileName || f.fileName || f.name || '',
						originalName: f.name || f.fileName || uploaded.fileName || ''
					})
				} catch (e) {
					uni.showToast({ title: e.message || '上传失败', icon: 'none' })
				}
			}
			this.$set(this.form, fieldKey, this.fileFields[fieldKey].map(x => x.ossId).join(','))
			this.currentFileFieldKey = ''
		},
		removeFile(fieldKey, idx) { const arr = (this.fileFields[fieldKey] || []).slice(); arr.splice(idx, 1); this.$set(this.fileFields, fieldKey, arr); this.$set(this.form, fieldKey, arr.map(x => x.ossId).join(',')) },
		openDateTime(fieldKey) { this.datetimeFieldKey = fieldKey; this.dateTimeSheetVisible = true },
		formatDateTime(key) { const v = this.form[key]; if (!v) return ''; return v.replace('T', ' ').substring(0, 16) },
		onDateTimeChange(val) { if (!this.datetimeFieldKey) return; this.$set(this.form, this.datetimeFieldKey, val); this.autoFillHours() },
		computedFieldText(key) { if (key === 'hours') { const val = this.form[key]; return val ? `${val} 小时` : '' } return this.form[key] || '' },
		openCity(fieldKey) { this.cityFieldKey = fieldKey; this.citySheetVisible = true },
		onCityChange(val) { if (!this.cityFieldKey) return; this.$set(this.form, this.cityFieldKey, val) },
		onFileSelect(fieldKey) { if (!this.fileFields[fieldKey]) this.$set(this.fileFields, fieldKey, []) },
		onFileSuccess(fieldKey, e) {
			if (!this.fileFields[fieldKey]) this.$set(this.fileFields, fieldKey, [])
			const files = e?.tempFiles || []
			const current = Array.isArray(this.fileFields[fieldKey]) ? this.fileFields[fieldKey] : []
			this.fileFields[fieldKey] = [...current, ...files.map(f => ({
				name: f.name || f.fileName || 'file',
				extname: (f.name || f.fileName || '').split('.').pop() || '',
				url: f.url || f.tempFilePath || f.path || '',
				ossId: f.ossId || f.fileID || f.url || '',
				fileName: f.fileName || f.name || '',
				originalName: f.originalName || f.name || f.fileName || ''
			}))]
			this.$set(this.form, fieldKey, this.fileFields[fieldKey].map(x => x.ossId).join(','))
		},
		onFileDelete(fieldKey, e) {
			const fileList = Array.isArray(this.fileFields[fieldKey]) ? this.fileFields[fieldKey] : []
			const index = e?.index ?? e?.indexs?.[0] ?? -1
			if (index >= 0) fileList.splice(index, 1)
			this.$set(this.fileFields, fieldKey, [...fileList])
			this.$set(this.form, fieldKey, fileList.map(x => x.ossId).join(','))
		},
		pickImages() {},


		autoFillHours() { 
			const s = this.parseDT(this.form.startTime), e = this.parseDT(this.form.endTime)
			if (!s || !e) return; 
			const ms = e.getTime() - s.getTime(); 
			if (ms <= 0) return; 
			const hours = (Math.round((ms / 3600000) * 2) / 2).toFixed(1).replace(/\.0$/, ''); 
			this.$set(this.form, 'hours', hours) 
		},
		parseDT(v) { if (!v) return null; const d = new Date(String(v).replace('T', ' ').replace(/-/g, '/')); return Number.isNaN(d.getTime()) ? null : d },
		submit() { this.autoFillHours(); for (const f of this.flowFields) if (f.required && !this.form[f.key]) return uni.showToast({ title: `请填写${f.label}`, icon: 'none' }); if (!this.assignee) return uni.showToast({ title: '请选择审批人', icon: 'none' }); this.doSubmit() },
		async doSubmit() { this.submitting = true; try { const payload = { ...this.form, status: 'pending', empId: this.currentEmpId, manualAssigneeUserId: this.assignee.userId || this.assignee.id }; const res = await this.requestApi(payload); const inst = res?.data || res; if (this.ccUsers.length && inst?.instId) await ccFlowTask({ instId: inst.instId, bizId: inst.bizId, bizType: this.bizType, ccUserIds: this.ccUsers.map(u => u.userId || u.id), remark: '手机端抄送', fromUserId: this.$store?.state?.user?.id || '', nodeId: 0, nodeName: '节点#0', readFlag: 0 }); uni.showToast({ title: '提交成功', icon: 'success' }); setTimeout(() => uni.navigateBack({ delta: 1 }), 500) } catch (e) { uni.showToast({ title: e.message || '提交失败', icon: 'none' }) } finally { this.submitting = false } }
	}
}
</script>

<style scoped>
.page{min-height:100vh;background:#f5f7fb}.scroll{height:100vh;padding:24rpx;box-sizing:border-box}.card{background:#fff;border-radius:20rpx;padding:24rpx;margin-bottom:20rpx}.card-title{font-size:30rpx;font-weight:700;color:#111827;margin-bottom:16rpx}.field{margin-bottom:18rpx}.label{display:block;margin-bottom:8rpx;font-size:24rpx;color:#6b7280}.req{color:#ef4444;margin-left:4rpx}.input-like,.input,.textarea{width:100%;box-sizing:border-box;border:1rpx solid #e5e7eb;border-radius:16rpx;background:#f9fafb;padding:20rpx;font-size:28rpx;color:#111827}.clickable{color:#111827}.textarea{min-height:160rpx}.computed-box{width:100%;box-sizing:border-box;padding:18rpx 20rpx;border-radius:16rpx;background:#eef6ff;color:#6b8fd6;font-size:26rpx;font-weight:500;line-height:1.5}.upload-box{border:1rpx dashed #cbd5e1;border-radius:18rpx;background:linear-gradient(180deg,#fbfdff 0%,#f4f8ff 100%);padding:24rpx 22rpx;box-shadow:0 8rpx 24rpx rgba(22,119,255,.06);}.upload-hint{font-size:28rpx;color:#111827;font-weight:700}.upload-sub{margin-top:8rpx;font-size:22rpx;color:#94a3b8;line-height:1.5}.file-list{margin-top:16rpx;display:flex;flex-direction:column;gap:12rpx}.file-item{display:flex;align-items:center;justify-content:space-between;padding:18rpx 18rpx;border-radius:16rpx;background:#f8fafc;border:1rpx solid #e5eefc}.file-name{flex:1;font-size:26rpx;color:#334155;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-right:16rpx}.remove{font-size:24rpx;color:#ef4444;flex-shrink:0}.btn.primary{width:100%;background:#1677ff;color:#fff;border-radius:16rpx}
</style>