feat(mobile-oa-h5): add kitchen-sink form page with OA type stubs
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
1b45fbaa36
commit
9adff5584e
4 files changed
+237
-2
No files matched your search
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div class="oa-stub">
|
||||
<div class="oa-stub__title">{{ title || typeName }}</div>
|
||||
<van-field
|
||||
v-model="inner"
|
||||
:placeholder="'[' + typeName + '] 占位,后续替换真实交互'"
|
||||
@update:model-value="onInput"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: [String, Number, Array, Object], default: '' },
|
||||
formCreateInject: { type: Object, default: null }
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const typeName = computed(
|
||||
() => props.formCreateInject?.rule?.type || 'unknown'
|
||||
)
|
||||
const title = computed(() => props.formCreateInject?.rule?.title || '')
|
||||
const inner = ref(
|
||||
typeof props.modelValue === 'string' || typeof props.modelValue === 'number'
|
||||
? String(props.modelValue ?? '')
|
||||
: JSON.stringify(props.modelValue ?? '')
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(v) => {
|
||||
inner.value =
|
||||
typeof v === 'string' || typeof v === 'number'
|
||||
? String(v ?? '')
|
||||
: JSON.stringify(v ?? '')
|
||||
}
|
||||
)
|
||||
|
||||
function onInput(v) {
|
||||
emit('update:modelValue', v)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.oa-stub {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.oa-stub__title {
|
||||
margin: 0 0 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,84 @@
|
||||
/** 按 zkxw-sc 已发布模板实际出现的 type 构造;非设计器理论全集 */
|
||||
export const kitchenSinkRule = [
|
||||
{ type: 'input', field: 'title', title: '单行文本', value: '', $required: true },
|
||||
{ type: 'textarea', field: 'remark', title: '多行文本', value: '' },
|
||||
{
|
||||
type: 'select',
|
||||
field: 'level',
|
||||
title: '下拉',
|
||||
value: '',
|
||||
options: [
|
||||
{ label: '普通', value: 'normal' },
|
||||
{ label: '紧急', value: 'urgent' }
|
||||
]
|
||||
},
|
||||
{ type: 'inputNumber', field: 'qty', title: '数字', value: 1 },
|
||||
{
|
||||
type: 'datePicker',
|
||||
field: 'bizDate',
|
||||
title: '日期',
|
||||
value: '',
|
||||
props: { type: 'date' }
|
||||
},
|
||||
{ type: 'upload', field: 'files', title: '附件 upload', value: [] },
|
||||
{ type: 'money', field: 'amount', title: '金额 money', value: '' },
|
||||
{
|
||||
type: 'relatedApproval',
|
||||
field: 'related',
|
||||
title: '关联审批 relatedApproval',
|
||||
value: []
|
||||
},
|
||||
{
|
||||
type: 'receiveAccount',
|
||||
field: 'payAccount',
|
||||
title: '收款账户 receiveAccount',
|
||||
value: []
|
||||
},
|
||||
{ type: 'dept', field: 'deptId', title: '部门 dept', value: '' },
|
||||
{ type: 'person', field: 'personIds', title: '人员 person', value: [] },
|
||||
{
|
||||
type: 'ownerPerson',
|
||||
field: 'ownerPerson',
|
||||
title: '所属人 ownerPerson',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
type: 'ownerDept',
|
||||
field: 'ownerDept',
|
||||
title: '所属部门 ownerDept',
|
||||
value: ''
|
||||
},
|
||||
{ type: 'post', field: 'postId', title: '岗位 post', value: '' },
|
||||
{ type: 'leaveKit', field: 'leaveKit', title: '请假套件 leaveKit', value: {} },
|
||||
{
|
||||
type: 'overtimeKit',
|
||||
field: 'overtimeKit',
|
||||
title: '加班套件 overtimeKit',
|
||||
value: {}
|
||||
},
|
||||
{
|
||||
type: 'confirmationKit',
|
||||
field: 'confirmationKit',
|
||||
title: '转正套件 confirmationKit',
|
||||
value: {}
|
||||
},
|
||||
{
|
||||
type: 'resignationKit',
|
||||
field: 'resignationKit',
|
||||
title: '离职套件 resignationKit',
|
||||
value: {}
|
||||
},
|
||||
{ type: 'tripKit', field: 'tripKit', title: '差旅套件 tripKit', value: {} },
|
||||
{
|
||||
type: 'tripItinerary',
|
||||
field: 'tripItinerary',
|
||||
title: '行程 tripItinerary',
|
||||
value: []
|
||||
}
|
||||
]
|
||||
|
||||
export const kitchenSinkOption = {
|
||||
submitBtn: false,
|
||||
resetBtn: false,
|
||||
form: { labelPosition: 'top' }
|
||||
}
|
||||
@@ -1,3 +1,27 @@
|
||||
import OaStubField from './components/OaStubField.vue'
|
||||
|
||||
/** 与 web-admin formCreate.component 名称对齐;阶段一可用 stub */
|
||||
const CUSTOM_TYPES = [
|
||||
'upload',
|
||||
'money',
|
||||
'relatedApproval',
|
||||
'receiveAccount',
|
||||
'dept',
|
||||
'person',
|
||||
'ownerPerson',
|
||||
'ownerDept',
|
||||
'post',
|
||||
'leaveKit',
|
||||
'overtimeKit',
|
||||
'confirmationKit',
|
||||
'resignationKit',
|
||||
'tripKit',
|
||||
'tripItinerary'
|
||||
]
|
||||
|
||||
export function registerOaComponents(formCreate) {
|
||||
// Task 3 will replace with real registrations
|
||||
if (!formCreate || typeof formCreate.component !== 'function') return
|
||||
for (const name of CUSTOM_TYPES) {
|
||||
formCreate.component(name, OaStubField)
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,75 @@
|
||||
<template>
|
||||
<div style="padding:16px">FormSink 占位(Task 3 实现)</div>
|
||||
<div class="sink">
|
||||
<header class="sink__hd">表单水槽测试</header>
|
||||
<div class="sink__body">
|
||||
<form-create-mobile
|
||||
v-model="formData"
|
||||
v-model:api="fapi"
|
||||
:rule="rule"
|
||||
:option="option"
|
||||
/>
|
||||
<pre class="sink__json">{{ preview }}</pre>
|
||||
</div>
|
||||
<footer class="sink__ft">
|
||||
<van-button block type="primary" @click="onValidate">校验并导出</van-button>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { showToast } from 'vant'
|
||||
import { kitchenSinkOption, kitchenSinkRule } from '../oa/kitchen-sink-rule'
|
||||
|
||||
const formData = ref({})
|
||||
const fapi = ref(null)
|
||||
const rule = ref(kitchenSinkRule)
|
||||
const option = ref(kitchenSinkOption)
|
||||
const preview = computed(() => JSON.stringify(formData.value, null, 2))
|
||||
|
||||
async function onValidate() {
|
||||
try {
|
||||
await fapi.value?.validate?.()
|
||||
showToast('校验通过')
|
||||
} catch (e) {
|
||||
showToast(e?.message || '校验失败')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sink {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--bg-page);
|
||||
}
|
||||
.sink__hd {
|
||||
padding: 14px 16px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
background: linear-gradient(135deg, var(--brand-green), var(--brand-green-dark));
|
||||
}
|
||||
.sink__body {
|
||||
flex: 1;
|
||||
padding: 12px 16px 88px;
|
||||
}
|
||||
.sink__json {
|
||||
margin-top: 16px;
|
||||
padding: 10px;
|
||||
overflow: auto;
|
||||
font-size: 12px;
|
||||
background: #fff;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
}
|
||||
.sink__ft {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 10px 16px calc(10px + env(safe-area-inset-bottom));
|
||||
background: #fff;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user