This commit is contained in:
@@ -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('测试模板');
|
||||
|
||||
@@ -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('');
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user