fix: 修复模板唯一性及六项运营页面问题
CSS quality / css-quality (push) Canceled after 0s

This commit is contained in:
hectorzhao
2026-09-21 19:28:50 +08:00
parent 28951b4fc4
commit 3cacb6e8e7
38 changed files with 1122 additions and 194 deletions
@@ -25,6 +25,24 @@ vi.mock('@/api/adminApi', () => ({
},
}));
describe('client template form', () => {
it('searches signatures and rejects invalid variable input and pasted placeholders', async () => {
render(<ClientTemplatesPage />);
await screen.findByText('测试模板');
fireEvent.click(screen.getByRole('button', { name: '编辑' }));
fireEvent.click(screen.getByRole('button', { name: /短信签名/ }));
fireEvent.change(screen.getByPlaceholderText('搜索短信签名'), { target: { value: '不存在' } });
expect(screen.getByText('无匹配选项')).toBeInTheDocument();
fireEvent.change(screen.getByPlaceholderText('搜索短信签名'), { target: { value: '签名' } });
fireEvent.click(screen.getByRole('option', { name: '【签名】' }));
fireEvent.click(screen.getByRole('button', { name: '插入变量' }));
const input = screen.getByRole('textbox', { name: '自定义变量名' });
fireEvent.change(input, { target: { value: '姓名' } });
expect(input).toHaveValue('');
fireEvent.change(input, { target: { value: 'code123' } });
expect(input).toHaveValue('code123');
fireEvent.change(screen.getByRole('textbox', { name: /模板内容/ }), { target: { value: '【签名】${中文}' } });
expect(screen.getByRole('button', { name: '提交审核' })).toBeDisabled();
});
it('orders requested fields and keeps standard deletion action', async () => {
render(<ClientTemplatesPage />);
await screen.findByText('测试模板');
+37 -3
View File
@@ -9,6 +9,11 @@ import {
} from '@/api/adminApi';
import { formatDateTime } from '@/utils/dateTime';
import { replaceLeadingSmsSignature } from '@/utils/smsSignature';
import {
templateVariableError,
templateVariableNameHint,
templateVariableNamePattern,
} from '@/utils/templateVariables';
import './ClientTemplatesPage.css';
type TemplateVariable = {
@@ -79,6 +84,7 @@ function TemplateModal({
signatures: ClientSmsSignatureView[];
}) {
const [customVariable, setCustomVariable] = useState('');
const [customVariableError, setCustomVariableError] = useState('');
const [variablesOpen, setVariablesOpen] = useState(false);
const contentRef = useRef<HTMLTextAreaElement>(null);
const initialSignature = signatures.find((signature) => signature.id === item?.signatureId);
@@ -99,6 +105,7 @@ function TemplateModal({
});
const [initialForm] = useState(form);
const dirty = JSON.stringify(form) !== JSON.stringify(initialForm);
const variableError = templateVariableError(form.content);
const application = applications.find((candidate) => candidate.id === form.applicationId);
const availableSignatures = signatures.filter(
(signature) =>
@@ -125,7 +132,11 @@ function TemplateModal({
}
function insertVariable(name: string) {
const normalized = name.trim();
const normalized = name;
if (!templateVariableNamePattern.test(normalized)) {
setCustomVariableError(templateVariableNameHint);
return;
}
if (!normalized) return;
const token = `\${${normalized}}`;
const textarea = contentRef.current;
@@ -154,7 +165,13 @@ function TemplateModal({
</Button>
<Button
disabled={!form.applicationId || !form.signatureId || !form.name || !form.content.trim()}
disabled={
!form.applicationId ||
!form.signatureId ||
!form.name.trim() ||
!form.content.trim() ||
Boolean(variableError)
}
onClick={() => onSubmit({ ...form, variables })}
>
@@ -184,6 +201,8 @@ function TemplateModal({
/>
<Select
label="短信签名"
searchable
searchPlaceholder="搜索短信签名"
onChange={(event) => selectSignature(event.target.value)}
options={[
{ label: '请选择签名', value: '' },
@@ -194,6 +213,7 @@ function TemplateModal({
/>
<Textarea
hint="模板内容必须以所选签名开头;选择或切换签名时系统会自动填入或替换完整签名,例如:【XX公司】验证码为${code}。"
error={variableError}
label="模板内容"
onChange={(event) => setContent(event.target.value)}
placeholder="请选择签名后填写正文,变量格式:${code}"
@@ -223,12 +243,26 @@ function TemplateModal({
<h3></h3>
<div className="template-custom-variable">
<Input
onChange={(event) => setCustomVariable(event.target.value)}
aria-label="自定义变量名"
error={customVariableError}
maxLength={32}
onChange={(event) => {
if (/[^A-Za-z0-9]/.test(event.target.value)) {
setCustomVariableError(templateVariableNameHint);
return;
}
setCustomVariableError('');
setCustomVariable(event.target.value);
}}
placeholder="英文字符或数字"
value={customVariable}
/>
<Button
onClick={() => {
if (!templateVariableNamePattern.test(customVariable)) {
setCustomVariableError(templateVariableNameHint);
return;
}
insertVariable(customVariable);
setCustomVariable('');
}}