mirror of https://gitee.com/answerdev/answer.git
commit
bf9ee2a4d0
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"github.copilot"
|
||||
]
|
||||
}
|
|
@ -326,7 +326,7 @@ ui:
|
|||
msg:
|
||||
empty: Code cannot be empty.
|
||||
language:
|
||||
label: Language (optional)
|
||||
label: Language
|
||||
placeholder: Automatic detection
|
||||
btn_cancel: Cancel
|
||||
btn_confirm: Add
|
||||
|
@ -362,7 +362,7 @@ ui:
|
|||
only_image: Only image files are allowed.
|
||||
max_size: File size cannot exceed 4MB.
|
||||
desc:
|
||||
label: Description (optional)
|
||||
label: Description
|
||||
tab_url: Image URL
|
||||
form_url:
|
||||
fields:
|
||||
|
@ -371,7 +371,7 @@ ui:
|
|||
msg:
|
||||
empty: Image URL cannot be empty.
|
||||
name:
|
||||
label: Description (optional)
|
||||
label: Description
|
||||
btn_cancel: Cancel
|
||||
btn_confirm: Add
|
||||
uploading: Uploading
|
||||
|
@ -391,7 +391,7 @@ ui:
|
|||
msg:
|
||||
empty: URL cannot be empty.
|
||||
name:
|
||||
label: Description (optional)
|
||||
label: Description
|
||||
btn_cancel: Cancel
|
||||
btn_confirm: Add
|
||||
ordered_list:
|
||||
|
@ -439,7 +439,7 @@ ui:
|
|||
range: URL slug up to 35 characters.
|
||||
character: URL slug contains unallowed character set.
|
||||
desc:
|
||||
label: Description (optional)
|
||||
label: Description
|
||||
btn_cancel: Cancel
|
||||
btn_submit: Submit
|
||||
tag_info:
|
||||
|
@ -456,10 +456,13 @@ ui:
|
|||
synonyms_text: The following tags will be remapped to
|
||||
delete:
|
||||
title: Delete this tag
|
||||
content: >-
|
||||
<p>We do not allowed deleting tag with posts.</p><p>Please remove this tag
|
||||
from the posts first.</p>
|
||||
content2: Are you sure you wish to delete?
|
||||
tip_with_posts: >-
|
||||
<p>We do not allowed <strong>deleting tag with posts</strong>.</p>
|
||||
<p>Please remove this tag from the posts first.</p>
|
||||
tip_with_synonyms: >-
|
||||
<p>We do not allowed <strong>deleting tag with synonyms</strong>.</p>
|
||||
<p>Please remove the synonyms from this tag first.</p>
|
||||
tip: Are you sure you wish to delete?
|
||||
close: Close
|
||||
edit_tag:
|
||||
title: Edit Tag
|
||||
|
@ -707,13 +710,13 @@ ui:
|
|||
default: System
|
||||
msg: Please upload an avatar
|
||||
bio:
|
||||
label: About Me (optional)
|
||||
label: About Me
|
||||
website:
|
||||
label: Website (optional)
|
||||
label: Website
|
||||
placeholder: "https://example.com"
|
||||
msg: Website incorrect format
|
||||
location:
|
||||
label: Location (optional)
|
||||
label: Location
|
||||
placeholder: "City, Country"
|
||||
notification:
|
||||
heading: Notifications
|
||||
|
@ -1210,11 +1213,11 @@ ui:
|
|||
validate: Please enter a valid URL.
|
||||
text: The address of your site.
|
||||
short_desc:
|
||||
label: Short Site Description (optional)
|
||||
label: Short Site Description
|
||||
msg: Short site description cannot be empty.
|
||||
text: "Short description, as used in the title tag on homepage."
|
||||
desc:
|
||||
label: Site Description (optional)
|
||||
label: Site Description
|
||||
msg: Site description cannot be empty.
|
||||
text: "Describe this site in one sentence, as used in the meta description tag."
|
||||
contact_email:
|
||||
|
@ -1224,14 +1227,6 @@ ui:
|
|||
text: Email address of key contact responsible for this site.
|
||||
interface:
|
||||
page_title: Interface
|
||||
logo:
|
||||
label: Logo (optional)
|
||||
msg: Site logo cannot be empty.
|
||||
text: You can upload your image or <1>reset</1> it to the site title text.
|
||||
theme:
|
||||
label: Theme
|
||||
msg: Theme cannot be empty.
|
||||
text: Select an existing theme.
|
||||
language:
|
||||
label: Interface Language
|
||||
msg: Interface language cannot be empty.
|
||||
|
@ -1283,18 +1278,18 @@ ui:
|
|||
branding:
|
||||
page_title: Branding
|
||||
logo:
|
||||
label: Logo (optional)
|
||||
label: Logo
|
||||
msg: Logo cannot be empty.
|
||||
text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown.
|
||||
mobile_logo:
|
||||
label: Mobile Logo (optional)
|
||||
label: Mobile Logo
|
||||
text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the "logo" setting will be used.
|
||||
square_icon:
|
||||
label: Square Icon (optional)
|
||||
label: Square Icon
|
||||
msg: Square icon cannot be empty.
|
||||
text: Image used as the base for metadata icons. Should ideally be larger than 512x512.
|
||||
favicon:
|
||||
label: Favicon (optional)
|
||||
label: Favicon
|
||||
text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, "square icon" will be used.
|
||||
legal:
|
||||
page_title: Legal
|
||||
|
@ -1361,6 +1356,7 @@ ui:
|
|||
text: Only logged in users can access this community.
|
||||
|
||||
form:
|
||||
optional: (optional)
|
||||
empty: cannot be empty
|
||||
invalid: is invalid
|
||||
btn_submit: Save
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
#spin-mask {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#protect-brower {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</noscript>
|
||||
<style>
|
||||
|
@ -49,11 +53,89 @@
|
|||
border-radius: 50%;
|
||||
animation: 0.75s linear infinite _doc-spin;
|
||||
}
|
||||
|
||||
#protect-brower {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
<div id="spin-container">
|
||||
<div class="spinner"></div>
|
||||
</div>
|
||||
<div id="protect-brower"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
/**
|
||||
* @description: Prompt that the browser version is too low
|
||||
*/
|
||||
const defaultList = [
|
||||
{
|
||||
name: 'Edge',
|
||||
version: '100'
|
||||
},
|
||||
{
|
||||
name: 'Firefox',
|
||||
version: '100'
|
||||
},
|
||||
{
|
||||
name: 'Chrome',
|
||||
version: '90'
|
||||
},
|
||||
{
|
||||
name: 'Safari',
|
||||
version: '15'
|
||||
}
|
||||
];
|
||||
function getBrowerTypeAndVersion(){
|
||||
var brower = {
|
||||
name: '',
|
||||
version: ''
|
||||
};
|
||||
var ua = navigator.userAgent.toLowerCase();
|
||||
var s;
|
||||
(s = ua.match(/edge\/([\d\.]+)/)) ? brower = { name: 'Edge', version: s[1] } :
|
||||
(s = ua.match(/firefox\/([\d\.]+)/)) ? brower = { name: 'Firefox', version: s[1] } :
|
||||
(s = ua.match(/chrome\/([\d\.]+)/)) ? brower = { name: 'Chrome', version: s[1] } :
|
||||
(s = ua.match(/version\/([\d\.]+).*safari/)) ? brower = { name: 'Safari', version: s[1] } : brower = { name: 'unknown', version: '' };
|
||||
// 根据关系进行判断
|
||||
return brower;
|
||||
}
|
||||
|
||||
function compareVersion(version1, version2) {
|
||||
var v1 = version1.split('.');
|
||||
var v2 = version2.split('.');
|
||||
var len = Math.max(v1.length, v2.length);
|
||||
while (v1.length < len) {
|
||||
v1.push('0');
|
||||
}
|
||||
while (v2.length < len) {
|
||||
v2.push('0');
|
||||
}
|
||||
for (var i = 0; i < len; i++) {
|
||||
var num1 = parseInt(v1[i]);
|
||||
var num2 = parseInt(v2[i]);
|
||||
if (num1 >= num2) {
|
||||
return 1;
|
||||
} else if (num1 < num2) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const browerInfo = getBrowerTypeAndVersion();
|
||||
const notSupport = defaultList.some(item => {
|
||||
if (item.name === browerInfo.name) {
|
||||
return compareVersion(browerInfo.version, item.version) === -1;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (notSupport) {
|
||||
const div = document.getElementById('protect-brower');
|
||||
div.innerText = 'The current browser version is too low, in order not to affect the normal use of the function, please upgrade the browser to the latest version.'
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
|
|
|
@ -239,7 +239,12 @@ const Code: FC<IEditorContext> = ({ editor, wrapText }) => {
|
|||
)}
|
||||
</Form.Group>
|
||||
<Form.Group controlId="editor.codeLanguageType" className="mb-3">
|
||||
<Form.Label>{t('code.form.fields.language.label')}</Form.Label>
|
||||
<Form.Label>{`${t('code.form.fields.language.label')} ${t(
|
||||
'optional',
|
||||
{
|
||||
keyPrefix: 'form',
|
||||
},
|
||||
)}`}</Form.Label>
|
||||
<Select
|
||||
options={codeLanguageType}
|
||||
value={lang}
|
||||
|
|
|
@ -41,7 +41,7 @@ const Image: FC<IEditorContext> = ({ editor }) => {
|
|||
|
||||
if (filteredFiles.length > 0) {
|
||||
AnswerModal.confirm({
|
||||
content: t('image.only_image'),
|
||||
content: t('image.form_image.fields.file.msg.only_image'),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ const Image: FC<IEditorContext> = ({ editor }) => {
|
|||
|
||||
if (filteredImages.length > 0) {
|
||||
AnswerModal.confirm({
|
||||
content: t('image.max_size'),
|
||||
content: t('image.form_image.fields.file.msg.max_size'),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
@ -96,13 +96,24 @@ const Image: FC<IEditorContext> = ({ editor }) => {
|
|||
const endPos = { ...startPos, ch: startPos.ch + loadingText.length };
|
||||
|
||||
editor.replaceSelection(loadingText);
|
||||
const urls = await upload(fileList);
|
||||
|
||||
const text = urls.map(({ name, url }) => {
|
||||
return `![${name}](${url})`;
|
||||
const urls = await upload(fileList).catch((ex) => {
|
||||
console.log('ex: ', ex);
|
||||
});
|
||||
|
||||
const text: string[] = [];
|
||||
if (Array.isArray(urls)) {
|
||||
urls.forEach(({ name, url }) => {
|
||||
if (name && url) {
|
||||
text.push(`![${name}](${url})`);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (text.length) {
|
||||
editor.replaceRange(text.join('\n'), startPos, endPos);
|
||||
} else {
|
||||
// Clear loading text
|
||||
editor.replaceRange('', startPos, endPos);
|
||||
}
|
||||
};
|
||||
|
||||
const paste = async (_, event) => {
|
||||
|
@ -251,7 +262,12 @@ const Image: FC<IEditorContext> = ({ editor }) => {
|
|||
|
||||
<Form.Group controlId="editor.imgDescription" className="mb-3">
|
||||
<Form.Label>
|
||||
{t('image.form_image.fields.desc.label')}
|
||||
{`${t('image.form_image.fields.desc.label')} ${t(
|
||||
'optional',
|
||||
{
|
||||
keyPrefix: 'form',
|
||||
},
|
||||
)}`}
|
||||
</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
|
@ -285,7 +301,9 @@ const Image: FC<IEditorContext> = ({ editor }) => {
|
|||
|
||||
<Form.Group controlId="editor.imgName" className="mb-3">
|
||||
<Form.Label>
|
||||
{t('image.form_url.fields.name.label')}
|
||||
{`${t('image.form_url.fields.name.label')} ${t('optional', {
|
||||
keyPrefix: 'form',
|
||||
})}`}
|
||||
</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
|
|
|
@ -88,7 +88,12 @@ const Link: FC<IEditorContext> = ({ editor }) => {
|
|||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="editor.internetSiteName" className="mb-3">
|
||||
<Form.Label>{t('link.form.fields.name.label')}</Form.Label>
|
||||
<Form.Label>{`${t('link.form.fields.name.label')} ${t(
|
||||
'optional',
|
||||
{
|
||||
keyPrefix: 'form',
|
||||
},
|
||||
)}`}</Form.Label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
value={name.value}
|
||||
|
|
|
@ -225,7 +225,9 @@ const useTagModal = (props: IProps = {}) => {
|
|||
</Form.Control.Feedback>
|
||||
</Form.Group>
|
||||
<Form.Group controlId="description">
|
||||
<Form.Label>{t('form.fields.desc.label')}</Form.Label>
|
||||
<Form.Label>{`${t('form.fields.desc.label')} ${t('optional', {
|
||||
keyPrefix: 'form',
|
||||
})}`}</Form.Label>
|
||||
<Form.Control
|
||||
className="font-monospace"
|
||||
value={formData.description.value}
|
||||
|
|
|
@ -44,22 +44,28 @@ const Index: FC = () => {
|
|||
properties: {
|
||||
logo: {
|
||||
type: 'string',
|
||||
title: t('logo.label'),
|
||||
title: `${t('logo.label')} ${t('optional', { keyPrefix: 'form' })}`,
|
||||
description: t('logo.text'),
|
||||
},
|
||||
mobile_logo: {
|
||||
type: 'string',
|
||||
title: t('mobile_logo.label'),
|
||||
title: `${t('mobile_logo.label')} ${t('optional', {
|
||||
keyPrefix: 'form',
|
||||
})}`,
|
||||
description: t('mobile_logo.text'),
|
||||
},
|
||||
square_icon: {
|
||||
type: 'string',
|
||||
title: t('square_icon.label'),
|
||||
title: `${t('square_icon.label')} ${t('optional', {
|
||||
keyPrefix: 'form',
|
||||
})}`,
|
||||
description: t('square_icon.text'),
|
||||
},
|
||||
favicon: {
|
||||
type: 'string',
|
||||
title: t('favicon.label'),
|
||||
title: `${t('favicon.label')} ${t('optional', {
|
||||
keyPrefix: 'form',
|
||||
})}`,
|
||||
description: t('favicon.text'),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -33,12 +33,16 @@ const General: FC = () => {
|
|||
},
|
||||
short_description: {
|
||||
type: 'string',
|
||||
title: t('short_desc.label'),
|
||||
title: `${t('short_desc.label')} ${t('optional', {
|
||||
keyPrefix: 'form',
|
||||
})}`,
|
||||
description: t('short_desc.text'),
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
title: t('desc.label'),
|
||||
title: `${t('desc.label')} ${t('optional', {
|
||||
keyPrefix: 'form',
|
||||
})}`,
|
||||
description: t('desc.text'),
|
||||
},
|
||||
contact_email: {
|
||||
|
|
|
@ -110,25 +110,25 @@ const Ask = () => {
|
|||
const handleTitleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
title: { ...formData.title, value: e.currentTarget.value },
|
||||
title: { ...formData.title, value: e.currentTarget.value, errorMsg: '' },
|
||||
});
|
||||
};
|
||||
const handleContentChange = (value: string) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
content: { ...formData.content, value },
|
||||
content: { ...formData.content, value, errorMsg: '' },
|
||||
});
|
||||
};
|
||||
const handleTagsChange = (value) =>
|
||||
setFormData({
|
||||
...formData,
|
||||
tags: { ...formData.tags, value },
|
||||
tags: { ...formData.tags, value, errorMsg: '' },
|
||||
});
|
||||
|
||||
const handleAnswerChange = (value: string) =>
|
||||
setFormData({
|
||||
...formData,
|
||||
answer: { ...formData.answer, value },
|
||||
answer: { ...formData.answer, value, errorMsg: '' },
|
||||
});
|
||||
|
||||
const handleSummaryChange = (evt: React.ChangeEvent<HTMLInputElement>) =>
|
||||
|
@ -140,55 +140,9 @@ const Ask = () => {
|
|||
},
|
||||
});
|
||||
|
||||
const checkValidated = (): boolean => {
|
||||
const bol = true;
|
||||
const { title, content, tags, answer } = formData;
|
||||
if (title.value && Array.from(title.value).length <= 150) {
|
||||
formData.title = {
|
||||
value: title.value,
|
||||
isInvalid: false,
|
||||
errorMsg: '',
|
||||
};
|
||||
}
|
||||
|
||||
if (content.value) {
|
||||
formData.content = {
|
||||
value: content.value,
|
||||
isInvalid: false,
|
||||
errorMsg: '',
|
||||
};
|
||||
}
|
||||
|
||||
if (Array.isArray(tags.value) && tags.value.length > 0) {
|
||||
formData.tags = {
|
||||
value: tags.value,
|
||||
isInvalid: false,
|
||||
errorMsg: '',
|
||||
};
|
||||
}
|
||||
|
||||
if (checked) {
|
||||
if (answer.value) {
|
||||
formData.answer = {
|
||||
value: answer.value,
|
||||
isInvalid: false,
|
||||
errorMsg: '',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
setFormData({
|
||||
...formData,
|
||||
});
|
||||
return bol;
|
||||
};
|
||||
|
||||
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (!checkValidated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const params: Type.QuestionParams = {
|
||||
title: formData.title.value,
|
||||
|
@ -232,7 +186,9 @@ const Ask = () => {
|
|||
})
|
||||
.catch((err) => {
|
||||
if (err.isError) {
|
||||
const data = handleFormError(err, formData);
|
||||
const data = handleFormError(err, formData, [
|
||||
{ from: 'content', to: 'answer' },
|
||||
]);
|
||||
setFormData({ ...data });
|
||||
}
|
||||
});
|
||||
|
|
|
@ -86,15 +86,27 @@ const TagIntroduction = () => {
|
|||
if (synonymsData?.synonyms && synonymsData.synonyms.length > 0) {
|
||||
Modal.confirm({
|
||||
title: t('delete.title'),
|
||||
content: t('delete.content2'),
|
||||
content: t('delete.tip_with_synonyms'),
|
||||
showConfirm: false,
|
||||
cancelText: t('delete.close'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (tagInfo.question_count > 0) {
|
||||
Modal.confirm({
|
||||
title: t('delete.title'),
|
||||
content: t('delete.content'),
|
||||
content: t('delete.tip_with_posts'),
|
||||
showConfirm: false,
|
||||
cancelText: t('delete.close'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Modal.confirm({
|
||||
title: t('delete.title'),
|
||||
content: t('delete.tip'),
|
||||
confirmText: t('delete', { keyPrefix: 'btns' }),
|
||||
confirmBtnVariant: 'danger',
|
||||
onConfirm: () => {
|
||||
deleteTag(tagInfo.tag_id);
|
||||
},
|
||||
|
|
|
@ -384,7 +384,11 @@ const Index: React.FC = () => {
|
|||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="bio" className="mb-3">
|
||||
<Form.Label>{t('bio.label')}</Form.Label>
|
||||
<Form.Label>
|
||||
{`${t('bio.label')} ${t('optional', {
|
||||
keyPrefix: 'form',
|
||||
})}`}
|
||||
</Form.Label>
|
||||
<Form.Control
|
||||
className="font-monospace"
|
||||
required
|
||||
|
@ -408,7 +412,9 @@ const Index: React.FC = () => {
|
|||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="website" className="mb-3">
|
||||
<Form.Label>{t('website.label')}</Form.Label>
|
||||
<Form.Label>{`${t('website.label')} ${t('optional', {
|
||||
keyPrefix: 'form',
|
||||
})}`}</Form.Label>
|
||||
<Form.Control
|
||||
required
|
||||
type="url"
|
||||
|
@ -431,7 +437,9 @@ const Index: React.FC = () => {
|
|||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="email" className="mb-3">
|
||||
<Form.Label>{t('location.label')}</Form.Label>
|
||||
<Form.Label>{`${t('location.label')} ${t('optional', {
|
||||
keyPrefix: 'form',
|
||||
})}`}</Form.Label>
|
||||
<Form.Control
|
||||
required
|
||||
type="text"
|
||||
|
|
|
@ -168,9 +168,16 @@ function labelStyle(color, hover) {
|
|||
function handleFormError(
|
||||
error: { list: Array<{ error_field: string; error_msg: string }> },
|
||||
data: any,
|
||||
keymap?: Array<{ from: string; to: string }>,
|
||||
) {
|
||||
if (error.list?.length > 0) {
|
||||
error.list.forEach((item) => {
|
||||
if (keymap?.length) {
|
||||
const key = keymap.find((k) => k.from === item.error_field);
|
||||
if (key) {
|
||||
item.error_field = key.to;
|
||||
}
|
||||
}
|
||||
const errorFieldObject = data[item.error_field];
|
||||
if (errorFieldObject) {
|
||||
errorFieldObject.isInvalid = true;
|
||||
|
|
Loading…
Reference in New Issue