feat(oa): add resignation kit with last_work_day writeback on approval
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
958a6a8cb8
commit
684f152683
22 files changed
+869
-4
No files matched your search
@@ -255,6 +255,7 @@ class UserCreateBody(BaseModel):
|
||||
phone_no: Optional[str] = Field("", alias="phoneNo")
|
||||
hire_date: Optional[str] = Field(None, alias="hireDate")
|
||||
probation_months: Optional[int] = Field(None, alias="probationMonths")
|
||||
last_work_day: Optional[str] = Field(None, alias="lastWorkDay")
|
||||
sex: Optional[str] = Field("0", alias="sex")
|
||||
avatar: Optional[str] = Field(None, alias="avatar")
|
||||
status: Optional[str] = Field("1", alias="status")
|
||||
@@ -273,6 +274,7 @@ class UserUpdateBody(BaseModel):
|
||||
phone_no: Optional[str] = Field(None, alias="phoneNo")
|
||||
hire_date: Optional[str] = Field(None, alias="hireDate")
|
||||
probation_months: Optional[int] = Field(None, alias="probationMonths")
|
||||
last_work_day: Optional[str] = Field(None, alias="lastWorkDay")
|
||||
sex: Optional[str] = Field(None, alias="sex")
|
||||
avatar: Optional[str] = Field(None, alias="avatar")
|
||||
status: Optional[str] = Field(None, alias="status")
|
||||
@@ -384,6 +386,7 @@ async def create_user(
|
||||
phone_no=body.phone_no,
|
||||
hire_date=body.hire_date,
|
||||
probation_months=body.probation_months,
|
||||
last_work_day=body.last_work_day,
|
||||
sex=body.sex,
|
||||
avatar=body.avatar,
|
||||
status=body.status,
|
||||
@@ -427,6 +430,8 @@ async def update_user(
|
||||
update_kwargs["hire_date"] = payload.get("hire_date")
|
||||
if "probation_months" in payload:
|
||||
update_kwargs["probation_months"] = payload.get("probation_months")
|
||||
if "last_work_day" in payload:
|
||||
update_kwargs["last_work_day"] = payload.get("last_work_day")
|
||||
obj = user_svc.update_user(db, user_id, **update_kwargs)
|
||||
return success_response(user_svc._row_to_dict(obj), message="更新成功")
|
||||
except ValueError as e:
|
||||
|
||||
@@ -61,6 +61,9 @@ def _build_user_info_payload(user: SysUser, web_role: str, role_names: list = No
|
||||
"email": (getattr(user, "email", None) or "").strip() or None,
|
||||
"hireDate": user.hire_date.strftime("%Y-%m-%d") if getattr(user, "hire_date", None) else None,
|
||||
"probationMonths": getattr(user, "probation_months", None),
|
||||
"lastWorkDay": user.last_work_day.strftime("%Y-%m-%d")
|
||||
if getattr(user, "last_work_day", None)
|
||||
else None,
|
||||
"sex": getattr(user, "sex", None) or "0",
|
||||
"remark": (getattr(user, "remark", None) or "").strip() or None,
|
||||
"role": web_role,
|
||||
|
||||
@@ -48,6 +48,9 @@ def _row_to_dict(u: SysUser, dept_name: str = "") -> Dict[str, Any]:
|
||||
"probationMonths": (
|
||||
u.probation_months if getattr(u, "probation_months", None) is not None else None
|
||||
),
|
||||
"lastWorkDay": u.last_work_day.strftime("%Y-%m-%d")
|
||||
if getattr(u, "last_work_day", None)
|
||||
else "",
|
||||
"sex": u.sex or "0",
|
||||
"avatar": u.avatar or "",
|
||||
"status": u.status,
|
||||
@@ -311,6 +314,7 @@ def create_user(
|
||||
phone_no: str = "",
|
||||
hire_date: Optional[Union[str, date]] = None,
|
||||
probation_months: Optional[int] = None,
|
||||
last_work_day: Optional[Union[str, date]] = None,
|
||||
sex: str = "0",
|
||||
avatar: Optional[str] = None,
|
||||
status: str = "1",
|
||||
@@ -342,6 +346,7 @@ def create_user(
|
||||
phone_no=(phone_no or "").strip(),
|
||||
hire_date=_parse_hire_date(hire_date),
|
||||
probation_months=probation_months,
|
||||
last_work_day=_parse_hire_date(last_work_day),
|
||||
sex=sex or "0",
|
||||
avatar=avatar,
|
||||
password=_hash_password(pw),
|
||||
@@ -375,6 +380,7 @@ def update_user(
|
||||
phone_no: Optional[str] = None,
|
||||
hire_date: Any = ...,
|
||||
probation_months: Any = ...,
|
||||
last_work_day: Any = ...,
|
||||
sex: Optional[str] = None,
|
||||
avatar: Optional[str] = None,
|
||||
status: Optional[str] = None,
|
||||
@@ -406,6 +412,8 @@ def update_user(
|
||||
obj.probation_months = None
|
||||
else:
|
||||
obj.probation_months = int(probation_months)
|
||||
if last_work_day is not ...:
|
||||
obj.last_work_day = _parse_hire_date(last_work_day)
|
||||
if sex is not None:
|
||||
obj.sex = sex
|
||||
if avatar is not None:
|
||||
|
||||
@@ -33,6 +33,7 @@ class SysUser(Base):
|
||||
phone_no = Column(String(11), nullable=False, default="", comment="手机号码")
|
||||
hire_date = Column(Date, nullable=True, comment="入职日期")
|
||||
probation_months = Column(Integer, nullable=True, comment="试用期月数")
|
||||
last_work_day = Column(Date, nullable=True, comment="最后工作日")
|
||||
sex = Column(CHAR(1), nullable=True, default="0", comment="用户性别")
|
||||
avatar = Column(String(512), nullable=True, comment="头像")
|
||||
password = Column("password", String(100), nullable=False, default="", comment="密码")
|
||||
|
||||
@@ -30,6 +30,7 @@ from app.services.oa_assignee import (
|
||||
)
|
||||
from app.services.oa_formula import FormulaError, apply_formulas_and_linkages, eval_formula
|
||||
from app.services.oa_confirmation_kit_sync import apply_confirmation_kit_user_sync
|
||||
from app.services.oa_resignation_kit_sync import apply_resignation_kit_user_sync
|
||||
|
||||
import logging
|
||||
|
||||
@@ -1647,6 +1648,12 @@ def advance_from(
|
||||
logger.exception(
|
||||
"confirmation kit user sync failed instance=%s", getattr(inst, "id", None)
|
||||
)
|
||||
try:
|
||||
apply_resignation_kit_user_sync(db, inst)
|
||||
except Exception:
|
||||
logger.exception(
|
||||
"resignation kit user sync failed instance=%s", getattr(inst, "id", None)
|
||||
)
|
||||
return
|
||||
if ntype == "condition":
|
||||
cur = next_node(cur)
|
||||
|
||||
@@ -60,6 +60,8 @@ LAYOUT_TYPES = frozenset(
|
||||
"object",
|
||||
"leaveKit",
|
||||
"confirmationKit",
|
||||
"overtimeKit",
|
||||
"resignationKit",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
"""离职套件:审批通过后回写发起人 last_work_day。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
from datetime import date, datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.models.models import OaAppVersion, OaInstance, SysUser
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_YMD = re.compile(r"^(\d{4})-(\d{2})-(\d{2})$")
|
||||
|
||||
|
||||
def _loads(raw: Any, default: Any = None) -> Any:
|
||||
if raw is None or raw == "":
|
||||
return default
|
||||
if isinstance(raw, (dict, list)):
|
||||
return raw
|
||||
try:
|
||||
return json.loads(raw)
|
||||
except Exception:
|
||||
return default
|
||||
|
||||
|
||||
def _extract_rules(form_schema: Any) -> List[Dict[str, Any]]:
|
||||
if form_schema is None:
|
||||
return []
|
||||
if isinstance(form_schema, list):
|
||||
return [r for r in form_schema if isinstance(r, dict)]
|
||||
if isinstance(form_schema, dict):
|
||||
rules = form_schema.get("rule") or form_schema.get("rules") or form_schema.get("fields")
|
||||
if isinstance(rules, list):
|
||||
return [r for r in rules if isinstance(r, dict)]
|
||||
if form_schema.get("type") or form_schema.get("field"):
|
||||
return [form_schema]
|
||||
return []
|
||||
|
||||
|
||||
def _walk(rules: List[Dict[str, Any]], out: List[Dict[str, Any]]) -> None:
|
||||
for rule in rules:
|
||||
out.append(rule)
|
||||
children = rule.get("children") or (rule.get("props") or {}).get("rule") or []
|
||||
if isinstance(children, list):
|
||||
_walk([c for c in children if isinstance(c, dict)], out)
|
||||
|
||||
|
||||
def _last_work_day_field(kit: Dict[str, Any]) -> Optional[str]:
|
||||
props = kit.get("props") if isinstance(kit.get("props"), dict) else {}
|
||||
if props.get("lastWorkDayField"):
|
||||
return str(props["lastWorkDayField"])
|
||||
children = kit.get("children") or props.get("rule") or []
|
||||
if isinstance(children, list):
|
||||
for c in children:
|
||||
if not isinstance(c, dict) or not c.get("field"):
|
||||
continue
|
||||
cprops = c.get("props") if isinstance(c.get("props"), dict) else {}
|
||||
if str(cprops.get("_fc_kit_field") or "") == "lastWorkDay":
|
||||
return str(c["field"])
|
||||
return None
|
||||
|
||||
|
||||
def _parse_ymd(value: Any) -> Optional[date]:
|
||||
if value is None or value == "":
|
||||
return None
|
||||
if isinstance(value, datetime):
|
||||
return value.date()
|
||||
if isinstance(value, date):
|
||||
return value
|
||||
s = str(value).strip()[:10]
|
||||
if not _YMD.match(s):
|
||||
return None
|
||||
try:
|
||||
return datetime.strptime(s, "%Y-%m-%d").date()
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
def extract_last_work_day(schema: Any, form_data: Dict[str, Any]) -> Optional[date]:
|
||||
flat: List[Dict[str, Any]] = []
|
||||
_walk(_extract_rules(schema), flat)
|
||||
for rule in flat:
|
||||
if str(rule.get("type") or "") != "resignationKit":
|
||||
continue
|
||||
field = _last_work_day_field(rule)
|
||||
if not field:
|
||||
continue
|
||||
parsed = _parse_ymd(form_data.get(field))
|
||||
if parsed is not None:
|
||||
return parsed
|
||||
return None
|
||||
|
||||
|
||||
def apply_resignation_kit_user_sync(db: Session, inst: OaInstance) -> None:
|
||||
try:
|
||||
if not inst or not getattr(inst, "id", None):
|
||||
return
|
||||
form_data = _loads(getattr(inst, "form_data_json", None), {}) or {}
|
||||
if not isinstance(form_data, dict):
|
||||
form_data = {}
|
||||
ver = (
|
||||
db.query(OaAppVersion)
|
||||
.filter(OaAppVersion.id == inst.app_version_id, OaAppVersion.del_flag == 0)
|
||||
.first()
|
||||
)
|
||||
schema = _loads(getattr(ver, "form_schema_json", None) if ver else None, {}) or {}
|
||||
last_day = extract_last_work_day(schema, form_data)
|
||||
if last_day is None:
|
||||
return
|
||||
user = (
|
||||
db.query(SysUser)
|
||||
.filter(
|
||||
SysUser.id == inst.initiator_user_id,
|
||||
SysUser.tenant_no == inst.tenant_no,
|
||||
SysUser.del_flag == 0,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if not user:
|
||||
logger.warning(
|
||||
"resignation kit sync: initiator not found id=%s",
|
||||
inst.initiator_user_id,
|
||||
)
|
||||
return
|
||||
user.last_work_day = last_day
|
||||
user.update_time = datetime.now()
|
||||
db.add(user)
|
||||
except Exception:
|
||||
logger.exception(
|
||||
"resignation kit user sync failed instance=%s", getattr(inst, "id", None)
|
||||
)
|
||||
@@ -0,0 +1,39 @@
|
||||
from datetime import date
|
||||
|
||||
from app.services.oa_resignation_kit_sync import extract_last_work_day
|
||||
|
||||
|
||||
def test_extract_last_work_day():
|
||||
schema = {
|
||||
"rule": [
|
||||
{
|
||||
"type": "resignationKit",
|
||||
"props": {"lastWorkDayField": "f_last"},
|
||||
"children": [
|
||||
{
|
||||
"type": "datePicker",
|
||||
"field": "f_last",
|
||||
"props": {"_fc_kit_field": "lastWorkDay"},
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
assert extract_last_work_day(schema, {"f_last": "2026-08-01"}) == date(2026, 8, 1)
|
||||
|
||||
|
||||
def test_empty_skipped():
|
||||
schema = {
|
||||
"rule": [
|
||||
{
|
||||
"type": "resignationKit",
|
||||
"props": {"lastWorkDayField": "f_last"},
|
||||
"children": [],
|
||||
}
|
||||
]
|
||||
}
|
||||
assert extract_last_work_day(schema, {"f_last": ""}) is None
|
||||
|
||||
|
||||
def test_no_kit():
|
||||
assert extract_last_work_day({"rule": [{"type": "input", "field": "x"}]}, {"x": 1}) is None
|
||||
@@ -0,0 +1,4 @@
|
||||
-- docs/sql/sys_user_last_work_day.sql
|
||||
-- 用户表补齐最后工作日(离职套件主数据)
|
||||
ALTER TABLE `sys_user`
|
||||
ADD COLUMN `last_work_day` date DEFAULT NULL COMMENT '最后工作日' AFTER `probation_months`;
|
||||
@@ -0,0 +1,202 @@
|
||||
# OA 表单设计 · 离职套件(Resignation Kit)设计文档
|
||||
|
||||
**日期**: 2026-07-30
|
||||
**状态**: 已实现
|
||||
**范围**: `web-admin` 用户管理补齐最后工作日;审批套件 Tab 离职套件;发起默认入职日期;`backend` 审批通过后回写发起人 `last_work_day`
|
||||
**界面模式**: 对齐请假/转正套件;视觉 Fresh Mint
|
||||
**前置**:
|
||||
- `docs/superpowers/specs/2026-07-30-oa-leave-kit-design.md`
|
||||
- `docs/superpowers/specs/2026-07-30-oa-confirmation-kit-design.md`(回写模式)
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
套件 Tab 增加 **离职套件**:采集入职日期、最后工作日、离职原因及备注;审批通过后将最后工作日回写发起人用户主数据。
|
||||
|
||||
### 1.1 目标
|
||||
|
||||
- `sys_user` 增补 **最后工作日**;用户管理新增/编辑/详情可维护。
|
||||
- 拖入离职套件得到 4 个子字段。
|
||||
- 发起时入职日期默认申请人(可改)。
|
||||
- 离职原因内置 5 项,设计器可维护 options。
|
||||
- 审批 **全部通过** 后仅回写发起人 `last_work_day`。
|
||||
|
||||
### 1.2 非目标(首期)
|
||||
|
||||
- 通过后自动停用账号。
|
||||
- 入职日期、离职原因、备注回写用户表。
|
||||
- 「最后工作日 ≥ 入职日期」交叉校验。
|
||||
- 代他人离职并回写他人主数据。
|
||||
|
||||
---
|
||||
|
||||
## 2. 产品决策(已确认)
|
||||
|
||||
| 项 | 决策 |
|
||||
|----|------|
|
||||
| 实现方案 | 套件容器 + 子字段拼装 |
|
||||
| 容器类型 | `resignationKit` |
|
||||
| 回写时机 | 审批全部通过 |
|
||||
| 回写对象 | 始终发起人 |
|
||||
| 回写字段 | 仅最后工作日 |
|
||||
| 账号状态 | 不动 |
|
||||
| 用户管理 | 最后工作日可新增/编辑/详情 |
|
||||
| 入职日期 | 默认申请人,可改,不回写 |
|
||||
| 最后工作日 | 默认必填(设计器可改);手填 |
|
||||
| 交叉校验 | 首期不做 |
|
||||
| 原因/备注 | 仅审批单 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 信息模型
|
||||
|
||||
### 3.1 心智模型
|
||||
|
||||
```
|
||||
┌─ 离职套件(resignationKit)──────────────────────────┐
|
||||
│ · datePicker 入职日期 ← userInfo.hireDate │
|
||||
│ · datePicker 最后工作日 ← 手填;通过后回写 │
|
||||
│ · select 离职原因 ← 5 默认项,可配置 │
|
||||
│ · textarea 离职原因备注 │
|
||||
└──────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
运行时不渲染套件边框/标题。
|
||||
|
||||
### 3.2 子字段
|
||||
|
||||
| 语义 key | title | type | 必填默认 | 回写 |
|
||||
|----------|-------|------|----------|------|
|
||||
| `hireDate` | 入职日期 | datePicker 仅日期 | 是 | ✗ |
|
||||
| `lastWorkDay` | 最后工作日 | datePicker 仅日期 | 是 | ✓ → `last_work_day` |
|
||||
| `resignReason` | 离职原因 | select | 是 | ✗ |
|
||||
| `resignRemark` | 离职原因备注 | textarea | 否 | ✗ |
|
||||
|
||||
离职原因默认 options:
|
||||
|
||||
- 家庭原因
|
||||
- 个人原因
|
||||
- 发展原因
|
||||
- 合同到期不续签
|
||||
- 其他
|
||||
|
||||
语义识别:`props._fc_kit_field`;容器 props 可挂各 `*Field` id。
|
||||
|
||||
### 3.3 Schema 示意
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "resignationKit",
|
||||
"props": {
|
||||
"hireDateField": "...",
|
||||
"lastWorkDayField": "...",
|
||||
"resignReasonField": "...",
|
||||
"resignRemarkField": "..."
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "datePicker",
|
||||
"title": "入职日期",
|
||||
"props": { "_fc_kit_field": "hireDate", "type": "date", "valueFormat": "YYYY-MM-DD" },
|
||||
"$required": true
|
||||
},
|
||||
{
|
||||
"type": "datePicker",
|
||||
"title": "最后工作日",
|
||||
"props": { "_fc_kit_field": "lastWorkDay", "type": "date", "valueFormat": "YYYY-MM-DD" },
|
||||
"$required": true
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"title": "离职原因",
|
||||
"options": [
|
||||
{ "label": "家庭原因", "value": "家庭原因" },
|
||||
{ "label": "个人原因", "value": "个人原因" },
|
||||
{ "label": "发展原因", "value": "发展原因" },
|
||||
{ "label": "合同到期不续签", "value": "合同到期不续签" },
|
||||
{ "label": "其他", "value": "其他" }
|
||||
],
|
||||
"props": { "_fc_kit_field": "resignReason" },
|
||||
"$required": true
|
||||
},
|
||||
{
|
||||
"type": "textarea",
|
||||
"title": "离职原因备注",
|
||||
"props": { "_fc_kit_field": "resignRemark", "rows": 3 },
|
||||
"$required": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 用户主数据
|
||||
|
||||
### 4.1 增列
|
||||
|
||||
```sql
|
||||
ALTER TABLE `sys_user`
|
||||
ADD COLUMN `last_work_day` date DEFAULT NULL COMMENT '最后工作日' AFTER `probation_months`;
|
||||
```
|
||||
|
||||
(若库中无 `probation_months`,则 AFTER `hire_date` 或 `phone_no`。)
|
||||
|
||||
同步:`SysUser`、`_row_to_dict`(`lastWorkDay`)、Create/Update Body、用户管理表单与详情、`userInfo` 可选带出 `lastWorkDay`(套件默认不依赖此项)。
|
||||
|
||||
---
|
||||
|
||||
## 5. 运行时
|
||||
|
||||
### 5.1 发起默认
|
||||
|
||||
仅对空的入职日期写入 `userInfo.hireDate`。其余字段不默认。
|
||||
|
||||
### 5.2 提交 / 发布
|
||||
|
||||
- 可见必填按 FormCreate 校验。
|
||||
- `LAYOUT_TYPES` 纳入 `resignationKit`。
|
||||
- 打印 unwrap 同其它套件。
|
||||
|
||||
### 5.3 通过后回写
|
||||
|
||||
挂在实例 `status = completed` 后(可与转正 sync 并列调用):
|
||||
|
||||
`apply_resignation_kit_user_sync(db, inst)`
|
||||
|
||||
- 扫描 `resignationKit` → 取 `lastWorkDay`
|
||||
- 有合法 `YYYY-MM-DD` 则更新发起人 `last_work_day`
|
||||
- 空 / 非法:跳过 + log;异常不回滚通过状态
|
||||
|
||||
---
|
||||
|
||||
## 6. 建议文件落点
|
||||
|
||||
| 区域 | 路径 |
|
||||
|------|------|
|
||||
| SQL | `docs/sql/sys_user_last_work_day.sql` |
|
||||
| 模型/CRUD/API/用户管理 | 同转正补字段路径 |
|
||||
| 容器 | `OaResignationKit.vue` |
|
||||
| DragRule / 注册 | `oa-resignation-kit-rule.ts`、`oa-form-create-resignation-kit.ts` |
|
||||
| 默认值 | `oa-resignation-kit-runtime.ts` |
|
||||
| 回写 | `oa_resignation_kit_sync.py` + `oa_engine` completed 钩子 |
|
||||
| 发布/打印 | `oa_publish.py`、`oa-approval-print.ts` |
|
||||
| 设计器拖入刷新 | `oa-fc-host/App.vue`(纳入 kit 名列表) |
|
||||
|
||||
---
|
||||
|
||||
## 7. 验收标准
|
||||
|
||||
1. 用户管理可维护最后工作日。
|
||||
2. 套件 Tab 可拖「离职」;原因默认 5 项且设计器可改。
|
||||
3. 发起:入职日期自动带出可改;最后工作日/原因/备注按规则校验。
|
||||
4. 通过前主数据最后工作日不变;通过后写入发起人;空不写。
|
||||
5. 入职日期、原因、备注不回写;账号状态不变。
|
||||
|
||||
---
|
||||
|
||||
## 8. 开放细节(不阻塞)
|
||||
|
||||
- `userInfo` 是否带出 `lastWorkDay`:可选,套件不依赖。
|
||||
- 与转正 sync 共用一个 hook 文件或并列两个函数:实现任选,逻辑分离即可。
|
||||
@@ -134,6 +134,7 @@ export function fetchCreateUser(data: {
|
||||
phoneNo?: string
|
||||
hireDate?: string
|
||||
probationMonths?: number | null
|
||||
lastWorkDay?: string
|
||||
sex?: string
|
||||
avatar?: string
|
||||
status?: string
|
||||
@@ -159,6 +160,7 @@ export function fetchUpdateUser(
|
||||
phoneNo: string
|
||||
hireDate: string | null
|
||||
probationMonths: number | null
|
||||
lastWorkDay: string | null
|
||||
sex: string
|
||||
avatar: string
|
||||
status: string
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<!-- 离职套件:仅组合容器 -->
|
||||
<template>
|
||||
<div class="oa-resignation-kit" v-bind="$attrs">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineOptions({ name: 'OaResignationKit', inheritAttrs: false })
|
||||
|
||||
defineProps<{
|
||||
hireDateField?: string
|
||||
lastWorkDayField?: string
|
||||
resignReasonField?: string
|
||||
resignRemarkField?: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.oa-resignation-kit {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -26,6 +26,8 @@ import { setupOaPostSelect } from '@/utils/oa-form-create-post'
|
||||
import { setupOaReceiveAccountSelect } from '@/utils/oa-form-create-receive-account'
|
||||
import { setupOaLeaveKit } from '@/utils/oa-form-create-leave-kit'
|
||||
import { setupOaConfirmationKit } from '@/utils/oa-form-create-confirmation-kit'
|
||||
import { setupOaOvertimeKit } from '@/utils/oa-form-create-overtime-kit'
|
||||
import { setupOaResignationKit } from '@/utils/oa-form-create-resignation-kit'
|
||||
|
||||
// FormCreate 按字符串解析 el-input / fc-radio 等,主应用是按需自动导入,不会全局注册;
|
||||
// 必须挂上官方 auto-import,否则发起审批/详情里动态表单控件会残缺(如单选项只剩文字)。
|
||||
@@ -42,6 +44,8 @@ setupOaPostSelect(FcDesigner.formCreate)
|
||||
setupOaReceiveAccountSelect(FcDesigner.formCreate)
|
||||
setupOaLeaveKit(FcDesigner.formCreate)
|
||||
setupOaConfirmationKit(FcDesigner.formCreate)
|
||||
setupOaOvertimeKit(FcDesigner.formCreate)
|
||||
setupOaResignationKit(FcDesigner.formCreate)
|
||||
|
||||
function maskSecret(value?: string) {
|
||||
if (!value) return '(empty)'
|
||||
|
||||
@@ -7,12 +7,13 @@
|
||||
height="100%"
|
||||
:config="designerConfig"
|
||||
:locale="fcLocale"
|
||||
@drag="onDesignerDrag"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { nextTick, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import formCreateZh from '@form-create/designer/locale/zh-cn'
|
||||
import { CONDITION_ELIGIBLE_TYPES } from '@/components/oa-approval/oa-condition-fields'
|
||||
@@ -27,18 +28,26 @@
|
||||
getOptionsJson?: () => string
|
||||
setRule?: (rule: unknown[]) => void
|
||||
setOption?: (option: unknown) => void
|
||||
activeMenuTab?: string
|
||||
}
|
||||
|
||||
const designerRef = ref<DesignerApi | null>(null)
|
||||
const fcLocale = formCreateZh
|
||||
let pendingSchema: { rule: unknown; option: unknown } | null = null
|
||||
let leaveKitRefreshTimer: number | null = null
|
||||
|
||||
/** 组件 Tab 不展示套件分组;套件 Tab 只展示 kit */
|
||||
const HIDDEN_ON_COMPONENT_TAB = ['kit']
|
||||
const HIDDEN_ON_KIT_TAB = ['main', 'aide', 'layout', 'subform']
|
||||
|
||||
const COND_FIELD = 'formCreateUseInCondition'
|
||||
|
||||
const designerConfig = {
|
||||
const designerConfig = reactive({
|
||||
showSaveBtn: false,
|
||||
showDevice: true,
|
||||
showAi: false,
|
||||
/** 默认在「组件」Tab:隐藏套件分组 */
|
||||
hiddenMenu: [...HIDDEN_ON_COMPONENT_TAB] as string[],
|
||||
/** 审批提交走流程,表单自身提交按钮默认关闭 */
|
||||
formOptions: {
|
||||
submitBtn: false
|
||||
@@ -65,6 +74,122 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* FormCreate 首次拖入带预置 children 的容器时,子规则未走 loadRule/makeRule,画布空白;
|
||||
* 切 Tab 触发 setRule 后才显示。拖入 leaveKit 后用 getRule→setRule 走与回显相同路径。
|
||||
*/
|
||||
function refreshLeaveKitCanvas() {
|
||||
const api = designerRef.value
|
||||
if (!api?.getRule || !api?.setRule) return
|
||||
try {
|
||||
const rules = api.getRule()
|
||||
const cloned = JSON.parse(JSON.stringify(rules || []))
|
||||
api.setRule(Array.isArray(cloned) ? cloned : [])
|
||||
} catch (e) {
|
||||
console.warn('[oa-fc-host] leaveKit canvas refresh failed', e)
|
||||
}
|
||||
}
|
||||
|
||||
function onDesignerDrag(payload: any) {
|
||||
const name =
|
||||
payload?.item?.name || payload?.dragRule?.name || payload?.menu?.name || ''
|
||||
if (
|
||||
name !== 'leaveKit' &&
|
||||
name !== 'overtimeKit' &&
|
||||
name !== 'confirmationKit' &&
|
||||
name !== 'resignationKit'
|
||||
)
|
||||
return
|
||||
if (leaveKitRefreshTimer != null) {
|
||||
window.clearTimeout(leaveKitRefreshTimer)
|
||||
}
|
||||
// drag 事件早于 children.splice,延后一帧再刷新
|
||||
leaveKitRefreshTimer = window.setTimeout(() => {
|
||||
leaveKitRefreshTimer = null
|
||||
nextTick(() => refreshLeaveKitCanvas())
|
||||
}, 30)
|
||||
}
|
||||
|
||||
/** 记住左侧面板 Tab,模块切换(国际化↔组件列表)重建 DOM 后恢复 */
|
||||
const leftPanelTab = ref<'menu' | 'kit' | 'tree'>('menu')
|
||||
|
||||
function getDesignerState(): any {
|
||||
const inst = designerRef.value as any
|
||||
if (!inst) return null
|
||||
return inst.setupState || inst.$?.setupState || inst
|
||||
}
|
||||
|
||||
function applyLeftPanelTab(tab: 'menu' | 'kit' | 'tree') {
|
||||
leftPanelTab.value = tab
|
||||
const host = document.querySelector('.oa-fc-host')
|
||||
host?.classList.remove('oa-fc-host--left-menu', 'oa-fc-host--left-kit', 'oa-fc-host--left-tree')
|
||||
const state = getDesignerState()
|
||||
if (tab === 'tree') {
|
||||
host?.classList.add('oa-fc-host--left-tree')
|
||||
if (state) state.activeMenuTab = 'tree'
|
||||
designerConfig.hiddenMenu = [...HIDDEN_ON_COMPONENT_TAB]
|
||||
} else if (tab === 'kit') {
|
||||
host?.classList.add('oa-fc-host--left-kit')
|
||||
if (state) state.activeMenuTab = 'menu'
|
||||
designerConfig.hiddenMenu = [...HIDDEN_ON_KIT_TAB]
|
||||
} else {
|
||||
host?.classList.add('oa-fc-host--left-menu')
|
||||
if (state) state.activeMenuTab = 'menu'
|
||||
designerConfig.hiddenMenu = [...HIDDEN_ON_COMPONENT_TAB]
|
||||
}
|
||||
}
|
||||
|
||||
/** 在 FormCreate「组件 | 大纲」之间插入「套件」Tab;DOM 被模块切换销毁后可重复注入 */
|
||||
function setupLeftPanelTabs() {
|
||||
const header = document.querySelector('.oa-fc-host ._fc-l-tabs') as HTMLElement | null
|
||||
if (!header) return false
|
||||
if (header.dataset.oaKitTabs === '1' && header.querySelector('.oa-fc-l-tabs')) {
|
||||
// 已注入:仅同步 hiddenMenu / activeMenuTab(防止仍停在套件过滤态却显示原生「组件」)
|
||||
applyLeftPanelTab(leftPanelTab.value)
|
||||
return true
|
||||
}
|
||||
|
||||
header.dataset.oaKitTabs = '1'
|
||||
header.querySelectorAll(':scope > ._fc-l-tab').forEach((el) => {
|
||||
;(el as HTMLElement).style.display = 'none'
|
||||
})
|
||||
// 清掉上次残留(极少见)
|
||||
header.querySelectorAll(':scope > .oa-fc-l-tabs').forEach((el) => el.remove())
|
||||
|
||||
const wrap = document.createElement('div')
|
||||
wrap.className = 'oa-fc-l-tabs'
|
||||
const cur = leftPanelTab.value
|
||||
wrap.innerHTML = [
|
||||
`<div class="oa-fc-l-tab${cur === 'menu' ? ' active' : ''}" data-oa-tab="menu">组件</div>`,
|
||||
`<div class="oa-fc-l-tab${cur === 'kit' ? ' active' : ''}" data-oa-tab="kit">套件</div>`,
|
||||
`<div class="oa-fc-l-tab${cur === 'tree' ? ' active' : ''}" data-oa-tab="tree">大纲</div>`
|
||||
].join('')
|
||||
header.appendChild(wrap)
|
||||
|
||||
wrap.addEventListener('click', (ev) => {
|
||||
const target = (ev.target as HTMLElement | null)?.closest?.(
|
||||
'[data-oa-tab]'
|
||||
) as HTMLElement | null
|
||||
if (!target) return
|
||||
const tab = target.dataset.oaTab as 'menu' | 'kit' | 'tree'
|
||||
wrap.querySelectorAll('.oa-fc-l-tab').forEach((el) => {
|
||||
el.classList.toggle('active', el === target)
|
||||
})
|
||||
applyLeftPanelTab(tab)
|
||||
})
|
||||
|
||||
// 从国际化等模块切回时,恢复上次 Tab,并同步 hiddenMenu
|
||||
applyLeftPanelTab(leftPanelTab.value || 'menu')
|
||||
return true
|
||||
}
|
||||
|
||||
function ensureLeftPanelTabs() {
|
||||
const header = document.querySelector('.oa-fc-host ._fc-l-tabs') as HTMLElement | null
|
||||
if (!header) return
|
||||
if (header.dataset.oaKitTabs === '1' && header.querySelector('.oa-fc-l-tabs')) return
|
||||
setupLeftPanelTabs()
|
||||
}
|
||||
|
||||
const MSG = {
|
||||
@@ -374,6 +499,7 @@
|
||||
}
|
||||
|
||||
let brandObserver: MutationObserver | null = null
|
||||
let tabsObserver: MutationObserver | null = null
|
||||
|
||||
onMounted(async () => {
|
||||
window.addEventListener('message', onMessage)
|
||||
@@ -382,12 +508,19 @@
|
||||
removeFormCreateBrand()
|
||||
brandObserver = new MutationObserver(() => removeFormCreateBrand())
|
||||
brandObserver.observe(document.body, { childList: true, subtree: true })
|
||||
// 切到「国际化」再回「组件列表」会销毁重建 ._fc-l-tabs,需持续补注套件 Tab
|
||||
tabsObserver = new MutationObserver(() => {
|
||||
ensureLeftPanelTabs()
|
||||
})
|
||||
tabsObserver.observe(document.body, { childList: true, subtree: true })
|
||||
setupLeftPanelTabs()
|
||||
window.parent?.postMessage({ type: MSG.READY }, '*')
|
||||
if (pendingSchema) {
|
||||
void applySchema(pendingSchema)
|
||||
}
|
||||
pollTimer = window.setInterval(() => {
|
||||
syncActivePanel()
|
||||
ensureLeftPanelTabs()
|
||||
}, 250)
|
||||
})
|
||||
|
||||
@@ -395,10 +528,16 @@
|
||||
window.removeEventListener('message', onMessage)
|
||||
brandObserver?.disconnect()
|
||||
brandObserver = null
|
||||
tabsObserver?.disconnect()
|
||||
tabsObserver = null
|
||||
if (pollTimer != null) {
|
||||
window.clearInterval(pollTimer)
|
||||
pollTimer = null
|
||||
}
|
||||
if (leaveKitRefreshTimer != null) {
|
||||
window.clearTimeout(leaveKitRefreshTimer)
|
||||
leaveKitRefreshTimer = null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -488,4 +627,38 @@
|
||||
.oa-fc-host ._fc-r {
|
||||
--el-color-primary: #10b981;
|
||||
}
|
||||
|
||||
/* 自研三 Tab:组件 | 套件 | 大纲(对齐 FormCreate 原 Tab 样式,主色 Mint) */
|
||||
.oa-fc-host ._fc-l-tabs {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.oa-fc-host .oa-fc-l-tabs {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.oa-fc-host .oa-fc-l-tab {
|
||||
flex: 1;
|
||||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.oa-fc-host .oa-fc-l-tab.active {
|
||||
color: #059669;
|
||||
border-bottom: 2px solid #10b981;
|
||||
}
|
||||
|
||||
/* 套件 Tab:隐藏分组标题「套件」,只保留卡片列表 */
|
||||
.oa-fc-host--left-kit ._fc-l-group > ._fc-l-title {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -23,6 +23,8 @@ import { setupOaPostSelect } from '@/utils/oa-form-create-post'
|
||||
import { setupOaReceiveAccountSelect } from '@/utils/oa-form-create-receive-account'
|
||||
import { setupOaLeaveKit } from '@/utils/oa-form-create-leave-kit'
|
||||
import { setupOaConfirmationKit } from '@/utils/oa-form-create-confirmation-kit'
|
||||
import { setupOaOvertimeKit } from '@/utils/oa-form-create-overtime-kit'
|
||||
import { setupOaResignationKit } from '@/utils/oa-form-create-resignation-kit'
|
||||
import App from './App.vue'
|
||||
|
||||
const app = createApp(App)
|
||||
@@ -49,4 +51,6 @@ setupOaPostSelect(FcDesigner.formCreate)
|
||||
setupOaReceiveAccountSelect(FcDesigner.formCreate)
|
||||
setupOaLeaveKit(FcDesigner.formCreate)
|
||||
setupOaConfirmationKit(FcDesigner.formCreate)
|
||||
setupOaOvertimeKit(FcDesigner.formCreate)
|
||||
setupOaResignationKit(FcDesigner.formCreate)
|
||||
app.mount('#app')
|
||||
Vendored
+1
@@ -45,6 +45,7 @@ declare namespace Api {
|
||||
phoneNo: string
|
||||
hireDate?: string
|
||||
probationMonths?: number | null
|
||||
lastWorkDay?: string
|
||||
sex: string
|
||||
avatar: string
|
||||
status: string
|
||||
|
||||
@@ -97,7 +97,9 @@ const SKIP_RULE_TYPES = new Set([
|
||||
'html',
|
||||
'vanCellGroup',
|
||||
'leaveKit',
|
||||
'confirmationKit'
|
||||
'confirmationKit',
|
||||
'overtimeKit',
|
||||
'resignationKit'
|
||||
])
|
||||
|
||||
export function resolveStamp(status: string | undefined | null): {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import FcDesigner from '@form-create/designer'
|
||||
import OaResignationKit from '@/components/oa-approval/OaResignationKit.vue'
|
||||
import { resignationKitDragRule } from '@/utils/oa-resignation-kit-rule'
|
||||
|
||||
const FLAG = '__oaResignationKitConfigured'
|
||||
|
||||
export function setupOaResignationKit(formCreate?: {
|
||||
component?: (name: string, c: unknown) => void
|
||||
}) {
|
||||
if ((FcDesigner as any)[FLAG]) return
|
||||
|
||||
if (typeof FcDesigner.addMenu === 'function') {
|
||||
try {
|
||||
FcDesigner.addMenu({ name: 'kit', title: '套件', list: [] })
|
||||
} catch {
|
||||
/* already */
|
||||
}
|
||||
}
|
||||
|
||||
FcDesigner.component('resignationKit', OaResignationKit)
|
||||
if (typeof formCreate?.component === 'function') {
|
||||
formCreate.component('resignationKit', OaResignationKit)
|
||||
}
|
||||
if (typeof FcDesigner.addDragRule === 'function') {
|
||||
FcDesigner.addDragRule(resignationKitDragRule)
|
||||
}
|
||||
;(FcDesigner as any)[FLAG] = true
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/** FormCreate Designer DragRule:离职套件 */
|
||||
|
||||
let _uidSeq = 0
|
||||
function uniqueId() {
|
||||
const num = 370 + ++_uidSeq
|
||||
return (
|
||||
'F' +
|
||||
Math.random().toString(36).slice(3, 6) +
|
||||
Number(`${Date.now()}`).toString(36) +
|
||||
num.toString(36) +
|
||||
'g'
|
||||
)
|
||||
}
|
||||
|
||||
export const RESIGN_REASON_OPTIONS = [
|
||||
{ label: '家庭原因', value: '家庭原因' },
|
||||
{ label: '个人原因', value: '个人原因' },
|
||||
{ label: '发展原因', value: '发展原因' },
|
||||
{ label: '合同到期不续签', value: '合同到期不续签' },
|
||||
{ label: '其他', value: '其他' }
|
||||
]
|
||||
|
||||
function buildResignationKitChildren() {
|
||||
const hireDateField = uniqueId()
|
||||
const lastWorkDayField = uniqueId()
|
||||
const resignReasonField = uniqueId()
|
||||
const resignRemarkField = uniqueId()
|
||||
return {
|
||||
hireDateField,
|
||||
lastWorkDayField,
|
||||
resignReasonField,
|
||||
resignRemarkField,
|
||||
children: [
|
||||
{
|
||||
type: 'datePicker',
|
||||
field: hireDateField,
|
||||
title: '入职日期',
|
||||
info: '',
|
||||
$required: true,
|
||||
props: {
|
||||
_fc_kit_field: 'hireDate',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
placeholder: '请选择入职日期'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'datePicker',
|
||||
field: lastWorkDayField,
|
||||
title: '最后工作日',
|
||||
info: '',
|
||||
$required: true,
|
||||
props: {
|
||||
_fc_kit_field: 'lastWorkDay',
|
||||
type: 'date',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
placeholder: '请选择最后工作日'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
field: resignReasonField,
|
||||
title: '离职原因',
|
||||
info: '',
|
||||
$required: true,
|
||||
options: RESIGN_REASON_OPTIONS.map((o) => ({ ...o })),
|
||||
props: {
|
||||
_fc_kit_field: 'resignReason',
|
||||
placeholder: '请选择离职原因',
|
||||
clearable: true
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'textarea',
|
||||
field: resignRemarkField,
|
||||
title: '离职原因备注',
|
||||
info: '',
|
||||
$required: false,
|
||||
props: {
|
||||
_fc_kit_field: 'resignRemark',
|
||||
rows: 3,
|
||||
placeholder: '可选'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export const resignationKitDragRule = {
|
||||
menu: 'kit' as const,
|
||||
icon: 'icon-user',
|
||||
label: '离职',
|
||||
name: 'resignationKit',
|
||||
input: false,
|
||||
drag: true,
|
||||
dragBtn: true,
|
||||
mask: false,
|
||||
inside: true,
|
||||
rule() {
|
||||
const built = buildResignationKitChildren()
|
||||
return {
|
||||
type: 'resignationKit',
|
||||
props: {
|
||||
hireDateField: built.hireDateField,
|
||||
lastWorkDayField: built.lastWorkDayField,
|
||||
resignReasonField: built.resignReasonField,
|
||||
resignRemarkField: built.resignRemarkField
|
||||
},
|
||||
children: built.children
|
||||
}
|
||||
},
|
||||
props() {
|
||||
return []
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/** 离职套件运行时:入职日期默认申请人 */
|
||||
|
||||
function asRules(schema: unknown): Record<string, unknown>[] {
|
||||
if (Array.isArray(schema)) return schema.filter((r) => r && typeof r === 'object') as any
|
||||
if (schema && typeof schema === 'object') {
|
||||
const s = schema as any
|
||||
const rules = s.rule || s.rules || s.fields
|
||||
if (Array.isArray(rules)) return rules.filter((r: any) => r && typeof r === 'object')
|
||||
if (s.type || s.field) return [s]
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
function walk(rules: Record<string, unknown>[], out: Record<string, unknown>[]) {
|
||||
for (const r of rules) {
|
||||
out.push(r)
|
||||
if (Array.isArray(r.children)) {
|
||||
walk(r.children as any, out)
|
||||
}
|
||||
const props = r.props as any
|
||||
if (props && Array.isArray(props.rule)) walk(props.rule, out)
|
||||
}
|
||||
}
|
||||
|
||||
export type ResignationKitBinding = {
|
||||
hireDateField?: string
|
||||
lastWorkDayField?: string
|
||||
}
|
||||
|
||||
function resolveBinding(kit: Record<string, unknown>): ResignationKitBinding {
|
||||
const props = (kit.props && typeof kit.props === 'object' ? kit.props : {}) as Record<
|
||||
string,
|
||||
any
|
||||
>
|
||||
let hireDateField = props.hireDateField ? String(props.hireDateField) : undefined
|
||||
let lastWorkDayField = props.lastWorkDayField ? String(props.lastWorkDayField) : undefined
|
||||
const children = (
|
||||
Array.isArray(kit.children)
|
||||
? kit.children
|
||||
: Array.isArray(props.rule)
|
||||
? props.rule
|
||||
: []
|
||||
) as Record<string, any>[]
|
||||
for (const c of children) {
|
||||
if (!c?.field) continue
|
||||
const sem = String(c.props?._fc_kit_field || '')
|
||||
if (sem === 'hireDate' && !hireDateField) hireDateField = String(c.field)
|
||||
if (sem === 'lastWorkDay' && !lastWorkDayField) lastWorkDayField = String(c.field)
|
||||
}
|
||||
return { hireDateField, lastWorkDayField }
|
||||
}
|
||||
|
||||
export function listResignationKitBindings(schema: unknown): ResignationKitBinding[] {
|
||||
const flat: Record<string, unknown>[] = []
|
||||
walk(asRules(schema), flat)
|
||||
return flat
|
||||
.filter((r) => String(r.type || '') === 'resignationKit')
|
||||
.map(resolveBinding)
|
||||
}
|
||||
|
||||
function isEmpty(raw: unknown): boolean {
|
||||
return raw == null || raw === ''
|
||||
}
|
||||
|
||||
/** 仅对空入职日期写入申请人 hireDate */
|
||||
export function applyResignationKitDefaults(
|
||||
schema: unknown,
|
||||
formData: Record<string, unknown>,
|
||||
user: Record<string, unknown> | null | undefined
|
||||
): Record<string, unknown> {
|
||||
const data = { ...formData }
|
||||
if (!user) return data
|
||||
const hireDate = String(user.hireDate || '').trim()
|
||||
if (!hireDate) return data
|
||||
for (const kit of listResignationKitBindings(schema)) {
|
||||
const f = kit.hireDateField
|
||||
if (f && isEmpty(data[f])) data[f] = hireDate
|
||||
}
|
||||
return data
|
||||
}
|
||||
@@ -171,6 +171,8 @@
|
||||
listLeaveKitBindings,
|
||||
markLeaveDurationManual
|
||||
} from '@/utils/oa-leave-kit-runtime'
|
||||
import { ensureOvertimeKitRuleProps } from '@/utils/oa-overtime-kit-runtime'
|
||||
import { applyResignationKitDefaults } from '@/utils/oa-resignation-kit-runtime'
|
||||
import {
|
||||
applyConfirmationKitDateLinkage,
|
||||
applyConfirmationKitDefaults,
|
||||
@@ -350,7 +352,9 @@
|
||||
: []
|
||||
formRule.value = ensureConfirmationKitVisibility(
|
||||
ensureOwnerFieldRuleProps(
|
||||
ensureLeaveKitRuleProps(ensureOaUploadRuleProps(rawRule))
|
||||
ensureOvertimeKitRuleProps(
|
||||
ensureLeaveKitRuleProps(ensureOaUploadRuleProps(rawRule))
|
||||
)
|
||||
)
|
||||
)
|
||||
const savedGutter = Number(schema.option?.row?.gutter)
|
||||
@@ -378,6 +382,11 @@
|
||||
data,
|
||||
(userStore.info || userStore.getUserInfo) as any
|
||||
)
|
||||
data = applyResignationKitDefaults(
|
||||
{ rule: formRule.value },
|
||||
data,
|
||||
(userStore.info || userStore.getUserInfo) as any
|
||||
)
|
||||
const linked = applyConfirmationKitDateLinkage({ rule: formRule.value }, data)
|
||||
formData.value = linked.data
|
||||
confirmationKitPrevSnapshot = { ...linked.data }
|
||||
|
||||
@@ -334,6 +334,16 @@
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="最后工作日">
|
||||
<el-date-picker
|
||||
v-model="formData.lastWorkDay"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="可选"
|
||||
class="w-full"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-select v-model="formData.sex" placeholder="请选择">
|
||||
<el-option label="男" value="0" />
|
||||
@@ -502,6 +512,7 @@
|
||||
phoneNo: '',
|
||||
hireDate: '' as string,
|
||||
probationMonths: null as number | null,
|
||||
lastWorkDay: '' as string,
|
||||
sex: '0',
|
||||
avatar: '',
|
||||
status: '1',
|
||||
@@ -609,6 +620,7 @@
|
||||
formData.phoneNo = ''
|
||||
formData.hireDate = ''
|
||||
formData.probationMonths = null
|
||||
formData.lastWorkDay = ''
|
||||
formData.sex = '0'
|
||||
formData.avatar = ''
|
||||
formData.status = '1'
|
||||
@@ -648,6 +660,7 @@
|
||||
detail.probationMonths === null || detail.probationMonths === undefined
|
||||
? null
|
||||
: Number(detail.probationMonths)
|
||||
formData.lastWorkDay = detail.lastWorkDay || ''
|
||||
formData.sex = detail.sex || '0'
|
||||
formData.avatar = detail.avatar || ''
|
||||
formData.status = detail.status || '1'
|
||||
@@ -683,6 +696,7 @@
|
||||
phoneNo: formData.phoneNo?.trim() || undefined,
|
||||
hireDate: formData.hireDate || null,
|
||||
probationMonths: formData.probationMonths,
|
||||
lastWorkDay: formData.lastWorkDay || null,
|
||||
sex: formData.sex,
|
||||
status: formData.status,
|
||||
orderNum: formData.orderNum,
|
||||
@@ -700,6 +714,7 @@
|
||||
phoneNo: formData.phoneNo?.trim() || undefined,
|
||||
hireDate: formData.hireDate || undefined,
|
||||
probationMonths: formData.probationMonths ?? undefined,
|
||||
lastWorkDay: formData.lastWorkDay || undefined,
|
||||
sex: formData.sex,
|
||||
status: formData.status,
|
||||
orderNum: formData.orderNum,
|
||||
@@ -811,6 +826,7 @@
|
||||
? '—'
|
||||
: `${row.probationMonths} 个月`
|
||||
},
|
||||
{ label: '最后工作日', value: row.lastWorkDay || '—' },
|
||||
{
|
||||
label: '性别',
|
||||
value:
|
||||
|
||||
Reference in new issue
Block a user