| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980 |
- <header class="resource-header">
- <div><p>DATA RESOURCE</p><h1>{{ schema()?.label || className() }}</h1><span>{{ className() }} · {{ page()?.total || 0 | number }} 条记录</span></div>
- <div class="header-actions"><button type="button" (click)="load(1, true)"><lucide-icon [img]="icons.RefreshCw" [size]="17" />刷新</button>@if (schema()?.writable && schema()?.creatable !== false) { <button class="primary" type="button" (click)="openCreate()"><lucide-icon [img]="icons.Plus" [size]="18" />新建记录</button> }</div>
- </header>
- <section class="resource-card">
- <div class="toolbar"><label><lucide-icon [img]="icons.Search" [size]="18" /><input [ngModel]="search()" (ngModelChange)="search.set($event)" (keyup.enter)="load(1)" placeholder="按名称、标题、账号或来源键搜索" /></label><button type="button" (click)="load(1)">查询</button></div>
- @if (error()) { <div class="inline-error">{{ error() }}</div> }
- @if (loading() && !page()) {
- <div class="empty"><lucide-icon class="spin" [img]="icons.LoaderCircle" [size]="26" />正在加载数据…</div>
- } @else {
- <div class="table-scroll"><table><thead><tr>@for (field of columns(); track field.name) { <th>{{ field.name }}</th> }<th class="actions">操作</th></tr></thead><tbody>
- @for (row of page()?.results || []; track row['objectId']) {
- <tr>@for (field of columns(); track field.name) { <td [title]="display(row[field.name])">{{ display(row[field.name]) }}</td> }<td class="actions"><button type="button" [attr.aria-label]="schema()?.writable ? '编辑' : '查看'" (click)="openEdit(row)"><lucide-icon [img]="schema()?.writable ? icons.Pencil : icons.Eye" [size]="16" /></button>@if (schema()?.writable) { <button class="danger" type="button" aria-label="删除" (click)="remove(row)"><lucide-icon [img]="icons.Trash2" [size]="16" /></button> }</td></tr>
- } @empty { <tr><td class="empty-cell" [attr.colspan]="columns().length + 1">没有符合条件的数据</td></tr> }
- </tbody></table></div>
- <footer class="pagination"><span>第 {{ page()?.page || 1 }} 页,共 {{ pageCount() }} 页</span><div><button type="button" [disabled]="(page()?.page || 1) <= 1 || loading()" (click)="load((page()?.page || 1) - 1)"><lucide-icon [img]="icons.ChevronLeft" [size]="17" />上一页</button><button type="button" [disabled]="(page()?.page || 1) * (page()?.pageSize || 20) >= (page()?.total || 0) || loading()" (click)="load((page()?.page || 1) + 1)">下一页<lucide-icon [img]="icons.ChevronRight" [size]="17" /></button></div></footer>
- }
- </section>
- @if (editorOpen()) {
- <button class="drawer-mask" type="button" aria-label="关闭编辑器" (click)="closeEditor()"></button>
- <aside class="editor-drawer">
- <header><div><span>{{ !schema()?.writable ? '查看记录' : draftId() ? '编辑记录' : '新建记录' }}</span><strong>{{ schema()?.label }}</strong></div><button type="button" (click)="closeEditor()"><lucide-icon [img]="icons.X" [size]="20" /></button></header>
- <div class="editor-fields">
- @if (className() === '_User' && !draftId()) {
- <section class="account-create">
- <div class="account-create-note"><strong>开通普通用户</strong><span>自动归入当前帐套与默认组 1,并初始化旧系统用户 ID、余额和经验字段。此入口不能授予管理员权限。</span></div>
- <label><span>登录账号 <small>3–64 位 · 必填</small></span><input autocomplete="off" [ngModel]="createUsername()" (ngModelChange)="createUsername.set($event)" /></label>
- <label><span>显示名 <small>选填</small></span><input autocomplete="off" [ngModel]="createDisplayName()" (ngModelChange)="createDisplayName.set($event)" /></label>
- <label><span>登录密码 <small>8–72 位 · 必填</small></span><input type="password" autocomplete="new-password" [ngModel]="createPassword()" (ngModelChange)="createPassword.set($event)" /></label>
- <label><span>确认密码 <small>必填</small></span><input type="password" autocomplete="new-password" [ngModel]="createConfirmPassword()" (ngModelChange)="createConfirmPassword.set($event)" /></label>
- <label><span>手机号 <small>选填</small></span><input inputmode="tel" autocomplete="off" [ngModel]="createMobile()" (ngModelChange)="createMobile.set($event)" /></label>
- <label><span>邀请账号 <small>选填 · 同帐套</small></span><input autocomplete="off" [ngModel]="createInviteUsername()" (ngModelChange)="createInviteUsername.set($event)" /></label>
- @if (createUserError()) { <div class="account-create-error">{{ createUserError() }}</div> }
- </section>
- } @else {
- @for (field of schema()?.fields || []; track field.name) { @if (field.writable) {
- <label><span>{{ field.name }} <small>{{ field.type }}{{ field.required ? ' · 必填' : '' }}</small></span>
- @if (field.type === 'Boolean') { <input type="checkbox" [ngModel]="draft()[field.name] === true" (ngModelChange)="setField(field, $event)" /> }
- @else if (field.type === 'Object' || field.type === 'Array') { <textarea rows="5" [class.invalid]="fieldErrors()[field.name]" [ngModel]="inputValue(field)" (ngModelChange)="setField(field, $event)"></textarea>@if (fieldErrors()[field.name]; as fieldError) { <small class="field-error">{{ fieldError }}</small> } }
- @else { <input [type]="field.type === 'Number' ? 'number' : field.type === 'Date' ? 'datetime-local' : 'text'" [ngModel]="inputValue(field)" (ngModelChange)="setField(field, $event)" [placeholder]="field.type === 'Pointer' ? (field.targetClass + ' objectId') : field.name" /> }
- </label>
- } }
- @if (className() === 'CommonModel' && draftId()) {
- <section class="content-controls">
- <header><div><strong>内容审核与归档</strong><span>专用云函数同步旧 ContentManage 状态与节点移动语义</span></div><b [class.recycled]="contentStatusValue() === -2">{{ contentStatusLabel(contentStatusValue()) }}</b></header>
- <label><span>审核状态</span><select [ngModel]="selectedContentStatus()" (ngModelChange)="selectedContentStatus.set($event)"><option [ngValue]="99">已审核(99)</option><option [ngValue]="0">待审核(0)</option><option [ngValue]="-1">未通过(-1)</option><option [ngValue]="-3">草稿(-3)</option></select></label>
- <button type="button" [disabled]="updatingContent()" (click)="applyContentAction('status')">更新状态</button>
- <label><span>目标节点 ID</span><input type="number" min="1" [ngModel]="targetContentNodeId()" (ngModelChange)="targetContentNodeId.set(+$event)" /></label>
- <button type="button" [disabled]="updatingContent() || !targetContentNodeId()" (click)="applyContentAction('move')">移动节点</button>
- <div class="content-recycle">@if (contentStatusValue() === -2) { <button type="button" [disabled]="updatingContent()" (click)="applyContentAction('recover')">从回收站恢复</button> } @else { <button class="danger-action" type="button" [disabled]="updatingContent()" (click)="applyContentAction('recycle')">移入回收站</button> }</div>
- @if (contentActionError()) { <div class="content-action-status error">{{ contentActionError() }}</div> }
- @if (contentActionMessage()) { <div class="content-action-status success">{{ contentActionMessage() }}</div> }
- </section>
- }
- @if (className() === '_User' && draftId()) {
- <section class="user-controls">
- <header><div><strong>账号状态与用户组</strong><span>专用云函数会同步旧版 State / GroupID;停用账号会立即撤销会话</span></div><b [class.disabled]="draft()['isDisabled'] === true">{{ draft()['isDisabled'] === true ? '已停用' : '正常' }}</b></header>
- <label class="reason-field"><span>状态变更原因 <small>选填 · 最多 200 字</small></span><input [ngModel]="userActionReason()" (ngModelChange)="userActionReason.set($event)" maxlength="200" /></label>
- <div class="status-actions">@if (draft()['isDisabled'] === true) { <button type="button" [disabled]="updatingUser()" (click)="setUserDisabled(false)">解除停用</button> } @else { <button class="danger-action" type="button" [disabled]="updatingUser()" (click)="setUserDisabled(true)">停用并撤销会话</button> }</div>
- <label class="group-field"><span>用户组</span><select [ngModel]="selectedUserGroupId()" (ngModelChange)="selectedUserGroupId.set($event)"><option [ngValue]="0">请选择用户组</option>@for (group of userGroups(); track group['objectId']) { <option [ngValue]="groupIdOf(group)">{{ groupLabel(group) }}</option> }</select>@if (!userGroups().length) { <small>当前帐套尚无 Group 记录,请先在“全部数据类”中建立用户组</small> }</label>
- <button class="group-submit" type="button" [disabled]="updatingUser() || !selectedUserGroupId()" (click)="moveUserGroup()">保存用户组</button>
- @if (userActionError()) { <div class="user-action-status error">{{ userActionError() }}</div> }
- @if (userActionMessage()) { <div class="user-action-status success">{{ userActionMessage() }}</div> }
- </section>
- <section class="password-reset">
- <header><lucide-icon [img]="icons.KeyRound" [size]="18" /><div><strong>重置登录密码</strong><span>通过专用云函数写入,不进入通用资料字段</span></div></header>
- <label><span>新密码 <small>至少 8 位</small></span><input type="password" autocomplete="new-password" [ngModel]="newPassword()" (ngModelChange)="newPassword.set($event)" /></label>
- <label><span>确认密码</span><input type="password" autocomplete="new-password" [ngModel]="confirmPassword()" (ngModelChange)="confirmPassword.set($event)" /></label>
- @if (passwordError()) { <div class="password-status error">{{ passwordError() }}</div> }
- @if (passwordMessage()) { <div class="password-status success">{{ passwordMessage() }}</div> }
- <button type="button" [disabled]="resettingPassword()" (click)="resetPassword()">{{ resettingPassword() ? '重置中…' : '确认重置密码' }}</button>
- </section>
- }
- }
- </div>
- <footer><button type="button" (click)="closeEditor()">{{ schema()?.writable ? '取消' : '关闭' }}</button>@if (schema()?.writable) { <button class="primary" type="button" [disabled]="saving()" (click)="submitEditor()">{{ saving() ? '保存中…' : className() === '_User' && !draftId() ? '开通用户' : '保存记录' }}</button> }</footer>
- </aside>
- }
|