vue3 monoca-editor SQL:自动补全、自定义颜色、格式化代码、标记错误

一、安装(注意webpack-plugin对应版本,否则不能运行)

参考 monaco-editor-webpack-plugin版本兼容

yarn add [email protected]

yarn add [email protected] -D

二、配置vue.config.js

const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin') module.exports = { configureWebpack: { plugins: [ new MonacoWebpackPlugin({ language: ['sql']
// 全局导入
import * as monaco from 'monaco-editor'
// 局部导入需要的功能和依赖
import * as monaco from 'monaco-editor/esm/vs/editor/edcore.main'
import 'monaco-editor/esm/vs/basic-languages/sql/sql.contribution'

2.封装组件

<template>
    <div id="code"></div>
</template>
<script>
  import Vue from 'vue'
  import {Component} from 'vue-property-decorator'
  import * as monaco from 'monaco-editor/esm/vs/editor/edcore.main'
  import 'monaco-editor/esm/vs/basic-languages/sql/sql.contribution'
  // import { language } from 'monaco-editor/esm/vs/basic-languages/sql/sql'
  // language.keywords插件自带关键词的不全,网上找了一份自己维护
  import sqlKeywords from './sqlKeywords.js'
  @Component({
    props: {
      database: String
 export default class SqlEditor extends Vue {
    editor = null
    color = null
    suggestion = null
    formatProvider = null
    mounted () {
      this.initEditor()
    // 父组件获取值
    getValue() {
      return this.editor.getValue()
    // 父组件设置值
    setValue(content) {
      this.editor.setValue(content)
    initEditor () {
      this.editor = monaco.editor.create(document.getElementById('code'), {
        //初始化配置
        value: '',
        theme: 'vs-dark',
        autoIndex: true,
        language: 'sql', // 语言类型
        tabCompletion: 'on',
        cursorSmoothCaretAnimation: true,
        formatOnPaste: true,
        mouseWheelZoom: true,
        folding: true, //代码折叠
        autoClosingBrackets: 'always',
        autoClosingOvertype: 'always',
        autoClosingQuotes: 'always',
        automaticLayout: 'always'
</script>
 

自定义提示

databaseData = {
  a: ['group', 'area'],
  b: ['user', 'client']
tableData = {
  user: ['age', 'gender'],
   group: ['id', 'name']
// 关键字
getSQLSuggest () {
  return sqlKeywords.map((key) => ({
    label: key,
    kind: monaco.languages.CompletionItemKind.Keyword,
    insertText: key,
    detail: 'keyword'
// 表名
getTableSuggest (dbName) {
  const tableNames = this.databaseData[dbName]
  if (!tableNames) {
    return []
  return tableNames.map((name) => ({
    label: name,
    kind: monaco.languages.CompletionItemKind.Constant,
    insertText: name,
    detail: dbName
// 字段名
getParamSuggest (tableName) {
  const params = this.tableData[tableName]
  if (!params) {
    return []
  return params.map((name) => ({
    label: name,
    kind: monaco.languages.CompletionItemKind.Constant,
    insertText: name,
    detail: 'param'
// 数据库名
getDBSuggest () {
  return Object.keys(this.databaseData).map((key) => ({
    label: key,
    kind: monaco.languages.CompletionItemKind.Enum,
    insertText: key,
    detail: 'database'
initEditor () {
      // 自动补全提示
      this.suggestion = monaco.languages.registerCompletionItemProvider('sql', {
        // 触发条件,也可以不写,不写的话只要输入满足配置的label就会提示;仅支持单字符
        triggerCharacters: ['.', ' '],
        provideCompletionItems: (model, position) => {
          let suggestions = []
          const { lineNumber, column } = position
          const textBeforePointer = model.getValueInRange({
            startLineNumber: lineNumber,
            startColumn: 0,
            endLineNumber: lineNumber,
            endColumn: column,
          const tokens = textBeforePointer.toLocaleLowerCase().trim().split(/\s+/)
          const lastToken = tokens[tokens.length - 1] // 获取最后一段非空字符串
          if (lastToken.endsWith('.')) {
            // 提示该数据库下的表名
            const tokenNoDot = lastToken.slice(0, lastToken.length - 1)
            if (Object.keys(this.databaseData).includes(tokenNoDot)) {
              suggestions = [...this.getTableSuggest(tokenNoDot)]
          } else if (lastToken === '.') {
            suggestions = []
          } else if (textBeforePointer.endsWith(' ')) {
            if (textBeforePointer.endsWith('select * from ')) {
              // select * from 提示指定数据库的表名
              suggestions = this.getTableSuggest(this.database)
            } else if (lastToken === 'where') {
              const lastToken2 = tokens[tokens.length - 2]
              const lastToken3 = tokens[tokens.length - 3]
              const lastToken4 = tokens[tokens.length - 4]
              const lastToken5 = tokens[tokens.length - 5]
              if (lastToken5 + lastToken4 + lastToken3 === 'select*from') {
                // select * from tableName where 提示指定表的字段名
                suggestions = [...this.getParamSuggest(lastToken2)]
              } else {
                suggestions = []
            }else {
              suggestions = []
          } else {
            // 提示数据库名和关键词
            suggestions = [...this.getDBSuggest(), ...this.getSQLSuggest()]
          return {
            suggestions,
      reg += '/'
      this.color = monaco.languages.setMonarchTokensProvider('sql', {
        ignoreCase: true,
        tokenizer: {
          root: [
              { token: 'keyword' },
            ], //蓝色
              /[+]|[-]|[*]|[/]|[%]|[>]|[<]|[=]|[!]|[:]|[&&]|[||]/,
              { token: 'string' },
            ], //红色
            [/'.*?'|".*?"/, { token: 'string.escape' }], //橙色
            [/#--.*?\--#/, { token: 'comment' }], //绿色
            [/null/, { token: 'regexp' }], //粉色
            [/[{]|[}]/, { token: 'type' }], //青色
            [/[\u4e00-\u9fa5]/, { token: 'predefined' }],//亮粉色
            [/''/, { token: 'invalid' }],//红色
            [/[\u4e00-\u9fa5]/, { token: 'number.binary' }],//浅绿
            [/(?!.*[a-zA-Z])[0-9]/, { token: 'number.hex' }], //浅绿
            [/[(]|[)]/, { token: 'number.octal' }], //浅绿
            [/[\u4e00-\u9fa5]/, { token: 'number.float' }],//浅绿
 

 格式化代码&标记错误

yarn add  [email protected] -D

import { format } from 'sql-formatter'
initEditor() {
 // 改写插件自带格式化功能
      const self = this
      this.formatProvider = monaco.languages.registerDocumentFormattingEditProvider('sql', {
        provideDocumentFormattingEdits(model) {
          return [{
            text: self.formatSql(1),
            range: model.getFullModelRange()
 // 格式化代码
    formatSql(needValue) {
      this.clearMistake()
      try {
        this.setValue(format((this.editor).getValue()))
      } catch (e) {
        const {message} = e
        const list = message.split(' ')
        const line = list.indexOf('line')
        const column = list.indexOf('column')
        this.markMistake({
          startLineNumber: Number(list[line + 1]),
          endLineNumber: Number(list[line + 1]),
          startColumn: Number(list[column + 1]),
          endColumn: Number(list[column + 1])
        }, 'Error', message)
      if (needValue) {
        return this.editor.getValue()
 // 标记错误信息
    markMistake(range, type, message) {
      const {startLineNumber, endLineNumber, startColumn, endColumn} = range
      monaco.editor.setModelMarkers(
        this.editor.getModel(),
        'eslint',
          startLineNumber,
          endLineNumber,
          startColumn,
          endColumn,
          severity: monaco.MarkerSeverity[type], // type可以是Error,Warning,Info
          message
    // 清除错误信息
    clearMistake() {
      monaco.editor.setModelMarkers(
        this.editor.getModel(),
        'eslint',
initEditor() {
    this.editor.onDidChangeModelContent(() => {
        console.log('value', this.editor.getValue())
 

销毁编辑器及其配置,防止自定义提示数据重复

beforeDestroy () {
  if (this.editor) {
    this.clearMistake()
    this.editor.dispose()
    this.color.dispose()
    this.suggestion.dispose()
    this.formatProvider.dispose()

--------------------------------------------------------------------------------------------------------------------------------

更新:附上SqlKeywords.js

// SqlKeywords.js
export default [
  'ACCESSIBLE',
  'ACCOUNT',
  'ACTION',
  'ADD',
  'AFTER',
  'AGAINST',
  'AGGREGATE',
  'ALGORITHM',
  'ALL',
  'ALTER',
  'ALWAYS',
  'ANALYSE',
  'ANALYZE',
  'AND',
  'ANY',
  'AS',
  'ASC',
  'ASCII',
  'ASENSITIVE',
  'AT',
  'AUTOEXTEND_SIZE',
  'AUTO_INCREMENT',
  'AVG',
  'AVG_ROW_LENGTH',
  'BACKUP',
  'BEFORE',
  'BEGIN',
  'BETWEEN',
  'BIGINT',
  'BINARY',
  'BINLOG',
  'BIT',
  'BLOB',
  'BLOCK',
  'BOOL',
  'BOOLEAN',
  'BOTH',
  'BTREE',
  'BY',
  'BYTE',
  'CACHE',
  'CALL',
  'CASCADE',
  'CASCADED',
  'CASE',
  'CATALOG_NAME',
  'CHAIN',
  'CHANGE',
  'CHANGED',
  'CHANNEL',
  'CHAR',
  'CHARACTER',
  'CHARSET',
  'CHECK',
  'CHECKSUM',
  'CIPHER',
  'CLASS_ORIGIN',
  'CLIENT',
  'CLOSE',
  'COALESCE',
  'CODE',
  'COLLATE',
  'COLLATION',
  'COLUMN',
  'COLUMNS',
  'COLUMN_FORMAT',
  'COLUMN_NAME',
  'COMMENT',
  'COMMIT',
  'COMMITTED',
  'COMPACT',
  'COMPLETION',
  'COMPRESSED',
  'COMPRESSION',
  'CONCURRENT',
  'CONDITION',
  'CONNECTION',
  'CONSISTENT',
  'CONSTRAINT',
  'CONSTRAINT_CATALOG',
  'CONSTRAINT_NAME',
  'CONSTRAINT_SCHEMA',
  'CONTAINS',
  'CONTEXT',
  'CONTINUE',
  'CONVERT',
  'CPU',
  'CREATE',
  'CROSS',
  'CUBE',
  'CURRENT',
  'CURRENT_DATE',
  'CURRENT_TIME',
  'CURRENT_TIMESTAMP',
  'CURRENT_USER',
  'CURSOR',
  'CURSOR_NAME',
  'DATA',
  'DATABASE',
  'DATABASES',
  'DATAFILE',
  'DATE',
  'DATETIME',
  'DAY',
  'DAY_HOUR',
  'DAY_MICROSECOND',
  'DAY_MINUTE',
  'DAY_SECOND',
  'DEALLOCATE',
  'DEC',
  'DECIMAL',
  'DECLARE',
  'DEFAULT',
  'DEFAULT_AUTH',
  'DEFINER',
  'DELAYED',
  'DELAY_KEY_WRITE',
  'DELETE',
  'DESC',
  'DESCRIBE',
  'DES_KEY_FILE',
  'DETERMINISTIC',
  'DIAGNOSTICS',
  'DIRECTORY',
  'DISABLE',
  'DISCARD',
  'DISK',
  'DISTINCT',
  'DISTINCTROW',
  'DIV',
  'DO',
  'DOUBLE',
  'DROP',
  'DUAL',
  'DUMPFILE',
  'DUPLICATE',
  'DYNAMIC',
  'EACH',
  'ELSE',
  'ELSEIF',
  'ENABLE',
  'ENCLOSED',
  'ENCRYPTION',
  'END',
  'ENDS',
  'ENGINE',
  'ENGINES',
  'ENUM',
  'ERROR',
  'ERRORS',
  'ESCAPE',
  'ESCAPED',
  'EVENT',
  'EVENTS',
  'EVERY',
  'EXCHANGE',
  'EXECUTE',
  'EXISTS',
  'EXIT',
  'EXPANSION',
  'EXPIRE',
  'EXPLAIN',
  'EXPORT',
  'EXTENDED',
  'EXTENT_SIZE',
  'FALSE',
  'FAST',
  'FAULTS',
  'FETCH',
  'FIELDS',
  'FILE',
  'FILE_BLOCK_SIZE',
  'FILTER',
  'FIRST',
  'FIXED',
  'FLOAT',
  'FLOAT4',
  'FLOAT8',
  'FLUSH',
  'FOLLOWS',
  'FOR',
  'FORCE',
  'FOREIGN',
  'FORMAT',
  'FOUND',
  'FROM',
  'FULL',
  'FULLTEXT',
  'FUNCTION',
  'GENERAL',
  'GENERATED',
  'GEOMETRY',
  'GEOMETRYCOLLECTION',
  'GET',
  'GET_FORMAT',
  'GLOBAL',
  'GRANT',
  'GRANTS',
  'GROUP',
  'GROUP_REPLICATION',
  'HANDLER',
  'HASH',
  'HAVING',
  'HELP',
  'HIGH_PRIORITY',
  'HOST',
  'HOSTS',
  'HOUR',
  'HOUR_MICROSECOND',
  'HOUR_MINUTE',
  'HOUR_SECOND',
  'IDENTIFIED',
  'IF',
  'IGNORE',
  'IGNORE_SERVER_IDS',
  'IMPORT',
  'IN',
  'INDEX',
  'INDEXES',
  'INFILE',
  'INITIAL_SIZE',
  'INNER',
  'INOUT',
  'INSENSITIVE',
  'INSERT',
  'INSERT_METHOD',
  'INSTALL',
  'INSTANCE',
  'INT',
  'INT1',
  'INT2',
  'INT3',
  'INT4',
  'INT8',
  'INTEGER',
  'INTERVAL',
  'INTO',
  'INVOKER',
  'IO',
  'IO_AFTER_GTIDS',
  'IO_BEFORE_GTIDS',
  'IO_THREAD',
  'IPC',
  'IS',
  'ISOLATION',
  'ISSUER',
  'ITERATE',
  'JOIN',
  'JSON',
  'KEY',
  'KEYS',
  'KEY_BLOCK_SIZE',
  'KILL',
  'LANGUAGE',
  'LAST',
  'LEADING',
  'LEAVE',
  'LEAVES',
  'LEFT',
  'LESS',
  'LEVEL',
  'LIKE',
  'LIMIT',
  'LINEAR',
  'LINES',
  'LINESTRING',
  'LIST',
  'LOAD',
  'LOCAL',
  'LOCALTIME',
  'LOCALTIMESTAMP',
  'LOCK',
  'LOCKS',
  'LOGFILE',
  'LOGS',
  'LONG',
  'LONGBLOB',
  'LONGTEXT',
  'LOOP',
  'LOW_PRIORITY',
  'MASTER',
  'MASTER_AUTO_POSITION',
  'MASTER_BIND',
  'MASTER_CONNECT_RETRY',
  'MASTER_DELAY',
  'MASTER_HEARTBEAT_PERIOD',
  'MASTER_HOST',
  'MASTER_LOG_FILE',
  'MASTER_LOG_POS',
  'MASTER_PASSWORD',
  'MASTER_PORT',
  'MASTER_RETRY_COUNT',
  'MASTER_SERVER_ID',
  'MASTER_SSL',
  'MASTER_SSL_CA',
  'MASTER_SSL_CAPATH',
  'MASTER_SSL_CERT',
  'MASTER_SSL_CIPHER',
  'MASTER_SSL_CRL',
  'MASTER_SSL_CRLPATH',
  'MASTER_SSL_KEY',
  'MASTER_SSL_VERIFY_SERVER_CERT',
  'MASTER_TLS_VERSION',
  'MASTER_USER',
  'MATCH',
  'MAXVALUE',
  'MAX_CONNECTIONS_PER_HOUR',
  'MAX_QUERIES_PER_HOUR',
  'MAX_ROWS',
  'MAX_SIZE',
  'MAX_STATEMENT_TIME',
  'MAX_UPDATES_PER_HOUR',
  'MAX_USER_CONNECTIONS',
  'MEDIUM',
  'MEDIUMBLOB',
  'MEDIUMINT',
  'MEDIUMTEXT',
  'MEMORY',
  'MERGE',
  'MESSAGE_TEXT',
  'MICROSECOND',
  'MIDDLEINT',
  'MIGRATE',
  'MINUTE',
  'MINUTE_MICROSECOND',
  'MINUTE_SECOND',
  'MIN_ROWS',
  'MOD',
  'MODE',
  'MODIFIES',
  'MODIFY',
  'MONTH',
  'MULTILINESTRING',
  'MULTIPOINT',
  'MULTIPOLYGON',
  'MUTEX',
  'MYSQL_ERRNO',
  'NAME',
  'NAMES',
  'NATIONAL',
  'NATURAL',
  'NCHAR',
  'NDB',
  'NDBCLUSTER',
  'NEVER',
  'NEW',
  'NEXT',
  'NO',
  'NODEGROUP',
  'NONBLOCKING',
  'NONE',
  'NOT',
  'NO_WAIT',
  'NO_WRITE_TO_BINLOG',
  'NULL',
  'NUMBER',
  'NUMERIC',
  'NVARCHAR',
  'OFFSET',
  'OLD_PASSWORD',
  'ON',
  'ONE',
  'ONLY',
  'OPEN',
  'OPTIMIZE',
  'OPTIMIZER_COSTS',
  'OPTION',
  'OPTIONALLY',
  'OPTIONS',
  'OR',
  'ORDER',
  'OUT',
  'OUTER',
  'OUTFILE',
  'OWNER',
  'PACK_KEYS',
  'PAGE',
  'PARSER',
  'PARSE_GCOL_EXPR',
  'PARTIAL',
  'PARTITION',
  'PARTITIONING',
  'PARTITIONS',
  'PASSWORD',
  'PHASE',
  'PLUGIN',
  'PLUGINS',
  'PLUGIN_DIR',
  'POINT',
  'POLYGON',
  'PORT',
  'PRECEDES',
  'PRECISION',
  'PREPARE',
  'PRESERVE',
  'PREV',
  'PRIMARY',
  'PRIVILEGES',
  'PROCEDURE',
  'PROCESSLIST',
  'PROFILE',
  'PROFILES',
  'PROXY',
  'PURGE',
  'QUARTER',
  'QUERY',
  'QUICK',
  'RANGE',
  'READ',
  'READS',
  'READ_ONLY',
  'READ_WRITE',
  'REAL',
  'REBUILD',
  'RECOVER',
  'REDOFILE',
  'REDO_BUFFER_SIZE',
  'REDUNDANT',
  'REFERENCES',
  'REGEXP',
  'RELAY',
  'RELAYLOG',
  'RELAY_LOG_FILE',
  'RELAY_LOG_POS',
  'RELAY_THREAD',
  'RELEASE',
  'RELOAD',
  'REMOVE',
  'RENAME',
  'REORGANIZE',
  'REPAIR',
  'REPEAT',
  'REPEATABLE',
  'REPLACE',
  'REPLICATE_DO_DB',
  'REPLICATE_DO_TABLE',
  'REPLICATE_IGNORE_DB',
  'REPLICATE_IGNORE_TABLE',
  'REPLICATE_REWRITE_DB',
  'REPLICATE_WILD_DO_TABLE',
  'REPLICATE_WILD_IGNORE_TABLE',
  'REPLICATION',
  'REQUIRE',
  'RESET',
  'RESIGNAL',
  'RESTORE',
  'RESTRICT',
  'RESUME',
  'RETURN',
  'RETURNED_SQLSTATE',
  'RETURNS',
  'REVERSE',
  'REVOKE',
  'RIGHT',
  'RLIKE',
  'ROLLBACK',
  'ROLLUP',
  'ROTATE',
  'ROUTINE',
  'ROW',
  'ROWS',
  'ROW_COUNT',
  'ROW_FORMAT',
  'RTREE',
  'SAVEPOINT',
  'SCHEDULE',
  'SCHEMA',
  'SCHEMAS',
  'SCHEMA_NAME',
  'SECOND',
  'SECOND_MICROSECOND',
  'SECURITY',
  'SELECT',
  'SENSITIVE',
  'SEPARATOR',
  'SERIAL',
  'SERIALIZABLE',
  'SERVER',
  'SESSION',
  'SET',
  'SHARE',
  'SHOW',
  'SHUTDOWN',
  'SIGNAL',
  'SIGNED',
  'SIMPLE',
  'SLAVE',
  'SLOW',
  'SMALLINT',
  'SNAPSHOT',
  'SOCKET',
  'SOME',
  'SONAME',
  'SOUNDS',
  'SOURCE',
  'SPATIAL',
  'SPECIFIC',
  'SQL',
  'SQLEXCEPTION',
  'SQLSTATE',
  'SQLWARNING',
  'SQL_AFTER_GTIDS',
  'SQL_AFTER_MTS_GAPS',
  'SQL_BEFORE_GTIDS',
  'SQL_BIG_RESULT',
  'SQL_BUFFER_RESULT',
  'SQL_CACHE',
  'SQL_CALC_FOUND_ROWS',
  'SQL_NO_CACHE',
  'SQL_SMALL_RESULT',
  'SQL_THREAD',
  'SQL_TSI_DAY',
  'SQL_TSI_HOUR',
  'SQL_TSI_MINUTE',
  'SQL_TSI_MONTH',
  'SQL_TSI_QUARTER',
  'SQL_TSI_SECOND',
  'SQL_TSI_WEEK',
  'SQL_TSI_YEAR',
  'SSL',
  'STACKED',
  'START',
  'STARTING',
  'STARTS',
  'STATS_AUTO_RECALC',
  'STATS_PERSISTENT',
  'STATS_SAMPLE_PAGES',
  'STATUS',
  'STOP',
  'STORAGE',
  'STORED',
  'STRAIGHT_JOIN',
  'STRING',
  'SUBCLASS_ORIGIN',
  'SUBJECT',
  'SUBPARTITION',
  'SUBPARTITIONS',
  'SUPER',
  'SUSPEND',
  'SWAPS',
  'SWITCHES',
  'TABLE',
  'TABLES',
  'TABLESPACE',
  'TABLE_CHECKSUM',
  'TABLE_NAME',
  'TEMPORARY',
  'TEMPTABLE',
  'TERMINATED',
  'TEXT',
  'THAN',
  'THEN',
  'TIME',
  'TIMESTAMP',
  'TIMESTAMPADD',
  'TIMESTAMPDIFF',
  'TINYBLOB',
  'TINYINT',
  'TINYTEXT',
  'TO',
  'TRAILING',
  'TRANSACTION',
  'TRIGGER',
  'TRIGGERS',
  'TRUE',
  'TRUNCATE',
  'TYPE',
  'TYPES',
  'UNCOMMITTED',
  'UNDEFINED',
  'UNDO',
  'UNDOFILE',
  'UNDO_BUFFER_SIZE',
  'UNICODE',
  'UNINSTALL',
  'UNION',
  'UNIQUE',
  'UNKNOWN',
  'UNLOCK',
  'UNSIGNED',
  'UNTIL',
  'UPDATE',
  'UPGRADE',
  'USAGE',
  'USE',
  'USER',
  'USER_RESOURCES',
  'USE_FRM',
  'USING',
  'UTC_DATE',
  'UTC_TIME',
  'UTC_TIMESTAMP',
  'VALIDATION',
  'VALUE',
  'VALUES',
  'VARBINARY',
  'VARCHAR',
  'VARCHARACTER',
  'VARIABLES',
  'VARYING',
  'VIEW',
  'VIRTUAL',
  'WAIT',
  'WARNINGS',
  'WEEK',
  'WEIGHT_STRING',
  'WHEN',
  'WHERE',
  'WHILE',
  'WITH',
  'WITHOUT',
  'WORK',
  'WRAPPER',
  'WRITE',
  'X509',
  'XA',
  'XID',
  'XML',
  'XOR',
  'YEAR',
  'YEAR_MONTH',
  'ZEROFILL'
        # javascript
      
GitHub 加速计划 / vu / vue
207.53 K
33.66 K vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。 最近提交 (Master分支:2 个月前 )
73486cb5 * chore: fix link broken Signed-off-by: snoppy <[email protected]> * Update packages/template-compiler/README.md [skip ci] --------- Signed-off-by: snoppy <[email protected]> Co-authored-by: Eduardo San Martin Morote <[email protected]> 4 个月前
e428d891 Updated Browser Compatibility reference. The previous currently returns HTTP 404. 4 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

  • 浏览量 5592
  • 收藏 0
  • 0

所有评论(0)