md文件(table)转js文件( object)【node脚本,日常tips】

该博客介绍了如何从Markdown文件中读取表格内容,并使用lodash库和函数组合技术将其转换为JavaScript对象,分别用于英语和中文本地化。转换后的JS文件包含键值对,方便于多语言资源管理。

一:效果

1.读取的的markdown文件

  • md视图
    在这里插入图片描述

  • md源码

| 英文  | 中文 |
| ----- | ---- |
| hello | 你好 |
| world | 世界 |

2.生成的js文件

  • en-US
export default {
  'hello': 'hello',
  'world': 'world',
}
  • zh-CN
export default {
  'hello': '你好',
  'world': '世界',
}

二:代码

注意:代码中使用了lodash库,以及函数组合、函数柯里化和正则技术。

1.util.js


const filterBlank = (item) => {return item != ''};

exports.filterBlank = filterBlank;

2.convertMdToJs.js

const fs = require('fs');
const lodash = require('lodash/fp');
const { filterBlank } = require('../spider/util');

const transformTable = (table) => {
  // lodash.split('| 英文  | 中文 |\r\n| ----- | ---- |\r\n')
  const dealWithStream = lodash.flowRight([lodash.map(transformTr), lodash.filter(filterBlank), lodash.split('\r\n')]);
  return dealWithStream(table);
}

const transformTr = (tr) => {
  const fn = lodash.flowRight(lodash.map(lodash.trim), lodash.filter(filterBlank), [lodash.split('|')])
  let data = fn(tr);
  return {
    'en-US': data[0],
    'zh-CN': data[1]
  }
}

const convertMdToJs = (mdDir, enDir, zhDir, mdFileName, jsFileName) =>{
  const mdFilePath = mdDir + mdFileName;
  const enFilePath = enDir + jsFileName;
  const zhFilePath = zhDir + jsFileName;
  // 1.读取数据
  let data = fs.readFileSync(mdFilePath, { encoding: 'utf-8' });
  // 2.转换数据
  let kvArr = transformTable(data);
  kvArr.shift();// 去除表头
  kvArr.shift();// 去除|...|...|
  let enFiledata = 'export default { \n';
  let zhFiledata = 'export default { \n';
  kvArr.map((item) => {
    const en = item['en-US'];
    const zh = item['zh-CN'];
    enFiledata += `  '${en}': '${en}',\n`;
    zhFiledata += `  '${en}': '${zh}',\n`;
  })
  enFiledata += '}';
  zhFiledata += '}';
  // 3.写入数据
  fs.writeFileSync(enFilePath,enFiledata);
  fs.writeFileSync(zhFilePath,zhFiledata);
}

exports.convertMdToJs = convertMdToJs;

3.script.js

const { convertMdToJs } = require('./convertMdToJs');
const lodash = require('lodash');
const fs = require('fs');

// 转换指定一个md文件
function convertOne(mdDir, enDir, zhDir, mdName, jsName) {
  try {
    convertMdToJs(mdDir, enDir, zhDir, mdName, jsName);
  } catch (e) {
    console.log(`${mdName} 转换失败,请检查`)
  }
}

// 转换一个文件夹的下的所有md文件(一级目录)
function convertAll(mdDir, enDir, zhDir) {
  // 柯里化
  const curriedConvertMdToJs = lodash.curry(convertMdToJs, 5);
  const mdToJs = curriedConvertMdToJs(mdDir, enDir, zhDir);// 注意相对路径容易报错
  const reg = /.md/;
  const fileArr = fs.readdirSync(mdDir);
  const mdFileArr = fileArr.filter((item) => {
    return reg.test(item)
  });
  mdFileArr.map((mdFileName) => {
    const jsFileName = mdFileName.split('.md')[0] + '.js';
    mdToJs(mdFileName, jsFileName)
  })
}

function main() {
  // test one
  // convertOne('C:\\Users\\22681\\Desktop\\mdToJs\\public\\', 'C:\\Users\\22681\\Desktop\\mdToJs\\locales\\en-US\\', 'C:\\Users\\22681\\Desktop\\mdToJs\\locales\\zh-CN\\','test.md', 'test.js');

  // test all
  convertAll('C:\\Users\\22681\\Desktop\\mdToJs\\public\\', 'C:\\Users\\22681\\Desktop\\mdToJs\\locales\\en-US\\', 'C:\\Users\\22681\\Desktop\\mdToJs\\locales\\zh-CN\\');
}


main()
我会给你一些代码,你需要把里面的中文都提取出来,每一个都作为object的key,对应的value是一个数组,数组第一个元素和key相同,第二个元素是key翻译出来的英文。import Layout from 'layouts'; const lockStatusMap = { 0: '未锁定', 1: '已锁定', 2: '其他用户锁定', }; export const lockStatusStyleMap = { 0: 'error', 1: 'success', 2: 'warning', }; export default function BatchImport() { const { userInfo } = useSelector((state) => state.modelUser); const router = useRouter(); const { query: { tableId, tableName }, } = router; const [tableData, setTableData] = useState([]); const [confirmLoading, setConfirmLoading] = useState(false); const [fileList, setFileList] = useState([]); const [uploadLoading, setUploadLoading] = useState(false); const [selectedRowKeys, setSelectedRowKeys] = useState([]); const [selectedRows, setSelectedRows] = useState([]); const access_token = localStorage.getItem('token'); const apiBaseUrl = process.env.API_BASE_URL; const { loading: commonDataLoading, dictItems, dynLookList, enumList, } = useSelector((state) => state.modelCommonData); const dispatch = useDispatch(); const { modelCommonData: { fetchCommonData }, } = dispatch; const { requesting: metaRequesting, launchRequest: getMetaRequest, res: metaRes } = useApiRequest(getMetaTableDetail); const { fields: originFields } = metaRes || {}; const { requesting: batchLockRequesting, launchRequest: batchLockRequest } = useApiRequest(batchLockData); const { requesting: batchCheckLockRequesting, launchRequest: batchCheckLockRequest } = useApiRequest(batchCheckLockSatus); useEffect(() => { fetchCommonData('dictItems'); fetchCommonData('dynLookList'); }, []); useEffect(() => { getMetaRequest({ tableId }).catch((error) => { message.error(error.msg || error.message || error.error || '查询失败'); }); }, [tableId]); const fields = useMemo(() => { if (!dictItems || !enumList || !dynLookList) { return []; } return ( originFields?.map((fieldInfo) => { const { visibleType, dynLookupId } = fieldInfo; let res = { ...fieldInfo, }; if (!!dynLookupId) { res.dynLookupInfo = dynLookList.find((item) => item.id === dynLookupId); } if (visibleType === 2) { const selectList = getSelectList(fieldInfo, dictItems, enumList); res.selectList = selectList; } return res; }) || [] ); }, [originFields, dictItems, enumList, dynLookList]); const columns = useMemo(() => { if (commonDataLoading.dictItems || commonDataLoading.enumList || commonDataLoading.dynLookList) { return []; } const lockStatusFilters = Object.keys(lockStatusMap).map((key) => ({ value: key, text: lockStatusMap[key] })); const lockStatusColumn = { title: '锁定状态', dataIndex: 'lockStatus', width: fieldWidth, render: (val) => lockStatusMap[val], render: (val) => <Badge status={lockStatusStyleMap[val]} text={lockStatusMap[val]} />, filters: lockStatusFilters, onFilter: (value, record) => record.lockStatus == value, }; const _columns = fields ?.filter((item) => item.fieldType === 1) ?.map((fieldInfo) => ({ title: ( <div className={!fieldInfo.isNullable && st.required}>{fieldInfo.displayName || fieldInfo.description}</div> ), dataIndex: fieldInfo.fieldNameCamel, width: fieldWidth, render: (val) => ( <div className={st.showValue} style={numberVisibleTypes.includes(fieldInfo.visibleType) ? { justifyContent: 'flex-end' } : {}} > {getShowValue(val, fieldInfo)} </div> ), })) || []; return [lockStatusColumn, ..._columns]; }, [fields, commonDataLoading]); const upload = async () => { const lists = tableData.filter((item) => item.lockStatus === 1); try { setConfirmLoading(true); await batchUpdateData(tableName, lists); setConfirmLoading(false); message.success('操作功'); setTimeout(() => { backPage(); }, 1000); } catch (error) { setConfirmLoading(false); message.error(error.msg || error.message || '操作失败'); } }; const confirmUpload = async () => { const recordIds = tableData.map((item) => item.id); const { lockedByCurrentUser, notLockedByCurrentUser, notFoundRecords } = await batchCheckLockRequest({ tableId, tableName, recordIds, }); const idInfo = originFields.find((item) => item.fieldNameCamel === 'id'); const errorTips = []; if (notLockedByCurrentUser.length > 0) { errorTips.push( `${idInfo.displayName || idInfo.description}为${notLockedByCurrentUser.join(',')}的${ notLockedByCurrentUser.length }条数据未被您锁定` ); } if (notFoundRecords.length > 0) { errorTips.push(`${idDescrition}为${notFoundRecords.join(',')}的${notFoundRecords.length}条数据未找到`); } if (errorTips.length > 0) { const canImportData = tableData.filter((item) => lockedByCurrentUser.includes(item.id)); return Modal.confirm({ centered: true, title: '提示', icon: <ExclamationCircleFilled />, okText: '继续', content: `${errorTips.join(',')},是否继续导入数据?`, onOk() { upload(canImportData); }, }); } upload(tableData); }; const backPage = () => { router.push(`/data/detail?tableId=${tableId}&tableName=${tableName}`); }; const onBeforeUpload = (file) => { const type = file.name.split('.').at(-1); let index = ['xlsx'].indexOf(type.toLocaleLowerCase()); if (index < 0) { message.error('文件类型不支持'); return false; } if (file.size > 10 * 1000 * 1000) { message.error('文件大小不超过10M哦'); return false; } setUploadLoading(true); return new Promise((resolve) => { resolve(file); }); }; const onUploadChange = (res) => { let { file: { status, response: { code, data, error } = {} } = {} } = res; if (status === 'uploading') { setFileList(res.fileList); } else if (status === 'done') { setUploadLoading(false); if (code === 1) { message.success('上传功'); setFileList(res.fileList); const _tableData = data.dataList?.map((item) => { const lockStatus = !item.lockedBy ? 0 : item.lockedBy === userInfo?.accountNumber ? 1 : 2; return { ...item, lockStatus, }; }) || []; setTableData(_tableData); } else { message.error(error); setFileList([]); } } else if (status === 'error') { setFileList([]); setUploadLoading(false); message.error('上传失败'); } }; const onRemove = () => { setTableData([]); setFileList([]); }; const onSelectChange = (newSelectedRowKeys, selectedRows) => { setSelectedRowKeys(newSelectedRowKeys); setSelectedRows(selectedRows); }; const rowSelection = { selectedRowKeys, onChange: onSelectChange, getCheckboxProps: (record) => ({ disabled: record.lockStatus !== 0, }), }; const handleBatchLock = () => { Modal.confirm({ title: '提示', content: '确定锁定选择的所有数据吗?锁定后将限制为仅您本人可编辑', onOk: async () => { await batchLockRequest({ tableId, tableName, recordIds: selectedRowKeys, }) .then((res) => { const { totalCount, successCount, details } = res; const failCount = totalCount - successCount; if (failCount === 0) { message.success('操作功'); } else { message.info(`${successCount}条数据锁定功,${failCount}条数据锁定失败`); } const newTableData = tableData.map((rowData) => { const newItem = details.find((item) => item.recordId === rowData.id); if (newItem) { const lockStatus = !newItem.lockBy ? 0 : newItem.lockBy === userInfo?.accountNumber ? 1 : 2; return { ...rowData, lockStatus, }; } else { return rowData; } }); setTableData(newTableData); setSelectedRowKeys([]); setSelectedRows([]); }) .catch((error) => { message.error(error.msg || error.message || error.error || '操作失败'); }); }, confirmLoading: batchLockRequesting, }); }; const unlockedNum = tableData.filter((item) => item.lockStatus === 0).length; const lockedNum = tableData.filter((item) => item.lockStatus === 1).length; const otherLockedNum = tableData.filter((item) => item.lockStatus === 2).length; return ( <div className={st.batchWrap}> <div className={st.uploadWrap}> <div className={st.title}>数据批量上传</div> <div className={st.line}></div> <div className={st.draggerWrap}> <Dragger name='file' fileList={fileList} headers={{ Authorization: `Bearer ${access_token}`, }} action={`${apiBaseUrl}/udop/api/data/${tableName}/v1/loadSheetForReview`} maxCount={1} onChange={onUploadChange} beforeUpload={onBeforeUpload} disabled={uploadLoading} onRemove={onRemove} > <img src={'/img/upload.svg'} className={st.uploadIcon} /> <div className={st.text}>点击或将文件拖拽到此区域上传</div> </Dragger> </div> <div className={st.tips}>注:请上传Excel文件文件大小不能超过10M,仅支持单次最多上传1000条数据。</div> </div> <div className={st.tableWrap}> <div className={st.titleWrap}> 匹配结果列表 <div className={st.btnWrap}> <Button onClick={backPage}>取消</Button> <Button type='primary' onClick={confirmUpload} disabled={!lockedNum} loading={confirmLoading}> 确认导入 </Button> </div> </div> <div className={st.summary}> <div className={st.block}> <Tooltip title='批量锁定'> <Button type='link' icon={<LockOutlined />} onClick={handleBatchLock} disabled={selectedRows.length === 0} loading={batchLockRequesting} /> </Tooltip> </div> <div className={st.block}> 已锁定 <span style={{ color: '#3095B4' }}>{lockedNum}</span>条 </div> <div className={st.block}> 未锁定 <span style={{ color: '#3095B4' }}>{unlockedNum}</span>条 </div> <div className={st.block}> 其他用户锁定 <span style={{ color: '#3095B4' }}>{otherLockedNum}</span>条 </div> <div className={st.block}> 可导入 <span style={{ color: '#3095B4' }}>{lockedNum}</span>条 </div> </div> {fields?.length === 0 ? ( <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> ) : ( <Table bordered dataSource={tableData} columns={columns} pagination={false} scroll={{ x: 'max-content', y: 'calc(100vh - 25.7rem)' }} rowSelection={rowSelection} rowKey='id' loading={metaRequesting || batchCheckLockRequesting || batchLockRequest} /> )} </div> </div> ); }
最新发布
11-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值